[sessionmgr][refactor] Use StoryModel for StoryState.

TEST=run_modular_tests.sh
MF-149 #done

Change-Id: I31f2cf585cc22ce5d62755c9c983e465caf20fa0
diff --git a/bin/sessionmgr/story/model/apply_mutations.cc b/bin/sessionmgr/story/model/apply_mutations.cc
index fefa18e..21c5b11 100644
--- a/bin/sessionmgr/story/model/apply_mutations.cc
+++ b/bin/sessionmgr/story/model/apply_mutations.cc
@@ -7,6 +7,7 @@
 #include "peridot/bin/sessionmgr/story/model/apply_mutations.h"
 
 using fuchsia::modular::StoryVisibilityState;
+using fuchsia::modular::StoryState;
 using fuchsia::modular::storymodel::StoryModel;
 using fuchsia::modular::storymodel::StoryModelMutation;
 
@@ -18,6 +19,10 @@
   story_model->set_visibility_state(visibility_state);
 }
 
+void ApplySetRuntimeState(const StoryState story_state, StoryModel* story_model) {
+  story_model->set_runtime_state(story_state);
+}
+
 }  // namespace
 
 StoryModel ApplyMutations(const StoryModel& current_model,
@@ -30,6 +35,9 @@
       case StoryModelMutation::Tag::kSetVisibilityState:
         ApplySetVisibilityState(command.set_visibility_state(), &new_model);
         break;
+      case StoryModelMutation::Tag::kSetRuntimeState:
+        ApplySetRuntimeState(command.set_runtime_state(), &new_model);
+        break;
       default:
         FXL_LOG(FATAL) << "Unsupported StoryModelMutation: "
                        << fidl::ToUnderlying(command.Which());
diff --git a/bin/sessionmgr/story/model/apply_mutations_unittest.cc b/bin/sessionmgr/story/model/apply_mutations_unittest.cc
index 3e708af..fc4ba17 100644
--- a/bin/sessionmgr/story/model/apply_mutations_unittest.cc
+++ b/bin/sessionmgr/story/model/apply_mutations_unittest.cc
@@ -10,6 +10,7 @@
 #include "gtest/gtest.h"
 #include "peridot/bin/sessionmgr/story/model/apply_mutations.h"
 
+using fuchsia::modular::StoryState;
 using fuchsia::modular::StoryVisibilityState;
 using fuchsia::modular::storymodel::StoryModel;
 using fuchsia::modular::storymodel::StoryModelMutation;
@@ -17,9 +18,21 @@
 namespace modular {
 namespace {
 
+// Test a single StoryModelMutation.set_runtimes_state command to change
+// StoryModel.runtime_state.
+TEST(ApplyMutationsTest, set_runtime_state) {
+  StoryModel before;
+  *before.mutable_runtime_state() = StoryState::STOPPED;
+
+  std::vector<StoryModelMutation> commands(1);
+  commands[0].set_set_runtime_state(StoryState::RUNNING);
+  auto result = ApplyMutations(before, commands);
+  EXPECT_EQ(StoryState::RUNNING, *result.runtime_state());
+}
+
 // Test a single StoryModelMutation.set_visibility_state command to change
 // StoryModel.visibility_state.
-TEST(ApplyMutationsTest, SingleMutation_set_visibility_state) {
+TEST(ApplyMutationsTest, set_visibility_state) {
   StoryModel before;
   *before.mutable_visibility_state() = StoryVisibilityState::DEFAULT;
 
diff --git a/bin/sessionmgr/story/model/story_model_owner.cc b/bin/sessionmgr/story/model/story_model_owner.cc
index 324c81e..4d84d47 100644
--- a/bin/sessionmgr/story/model/story_model_owner.cc
+++ b/bin/sessionmgr/story/model/story_model_owner.cc
@@ -18,8 +18,11 @@
 namespace modular {
 
 namespace {
-// Sets default values for all fields of a new StoryModel.
+// Sets default values for all fields of a new StoryModel. Defaults are
+// documented in
+// peridot/lib/fidl/public/fuchsia.modular.storymodel/story_model.fidl.
 void InitializeModelDefaults(StoryModel* model) {
+  model->set_runtime_state(fuchsia::modular::StoryState::STOPPED);
   model->set_visibility_state(fuchsia::modular::StoryVisibilityState::DEFAULT);
   model->set_modules(fidl::VectorPtr<ModuleModel>::New(0));
 }
diff --git a/bin/sessionmgr/story/model/story_mutator.cc b/bin/sessionmgr/story/model/story_mutator.cc
index f89c9ca..c91c1e0 100644
--- a/bin/sessionmgr/story/model/story_mutator.cc
+++ b/bin/sessionmgr/story/model/story_mutator.cc
@@ -13,6 +13,13 @@
 StoryMutator::StoryMutator() = default;
 StoryMutator::~StoryMutator() = default;
 
+fit::consumer<> StoryMutator::set_runtime_state(
+    fuchsia::modular::StoryState state) {
+  std::vector<StoryModelMutation> commands(1);
+  commands[0].set_set_runtime_state(state);
+  return ExecuteInternal(std::move(commands));
+}
+
 fit::consumer<> StoryMutator::set_visibility_state(
     fuchsia::modular::StoryVisibilityState state) {
   std::vector<StoryModelMutation> commands(1);
diff --git a/bin/sessionmgr/story/model/story_mutator.h b/bin/sessionmgr/story/model/story_mutator.h
index c0f24a6..58f6943 100644
--- a/bin/sessionmgr/story/model/story_mutator.h
+++ b/bin/sessionmgr/story/model/story_mutator.h
@@ -39,6 +39,10 @@
   // A failure guarantees that the mutation was not applied and it is safe to
   // retry.
 
+  // Sets the value of |StoryModel.runtime_state|.
+  fit::consumer<> set_runtime_state(
+      fuchsia::modular::StoryState state);
+
   // Sets the value of |StoryModel.visibility_state|.
   fit::consumer<> set_visibility_state(
       fuchsia::modular::StoryVisibilityState state);
diff --git a/bin/sessionmgr/story_runner/story_controller_impl.cc b/bin/sessionmgr/story_runner/story_controller_impl.cc
index 426297d..f800ca4 100644
--- a/bin/sessionmgr/story_runner/story_controller_impl.cc
+++ b/bin/sessionmgr/story_runner/story_controller_impl.cc
@@ -506,7 +506,8 @@
       return;
     }
 
-    story_controller_impl_->SetState(fuchsia::modular::StoryState::STOPPING);
+    story_controller_impl_->SetRuntimeState(
+        fuchsia::modular::StoryState::STOPPING);
 
     // If this StopCall is part of a bulk operation of story provider that stops
     // all stories at once, no DetachView() notification is given to the session
@@ -611,7 +612,7 @@
           // been destroyed at this point.
           FXL_DCHECK(story_controller_impl_->ongoing_activities_.size() == 0);
 
-          story_controller_impl_->SetState(
+          story_controller_impl_->SetRuntimeState(
               fuchsia::modular::StoryState::STOPPED);
 
           story_controller_impl_->DestroyStoryEnvironment();
@@ -1050,7 +1051,7 @@
                 nullptr /* module_controller_request */, [flow] {}));
           }
 
-          story_controller_impl_->SetState(
+          story_controller_impl_->SetRuntimeState(
               fuchsia::modular::StoryState::RUNNING);
         });
   }
@@ -1185,19 +1186,22 @@
 };
 
 StoryControllerImpl::StoryControllerImpl(
-    fidl::StringPtr story_id, SessionStorage* const session_storage,
-    StoryStorage* const story_storage,
+    SessionStorage* const session_storage, StoryStorage* const story_storage,
+    std::unique_ptr<StoryMutator> story_mutator,
+    std::unique_ptr<StoryObserver> story_observer,
     StoryVisibilitySystem* const story_visibility_system,
     StoryProviderImpl* const story_provider_impl)
-    : story_id_(story_id),
+    : story_id_(*story_observer->model().name()),
       story_provider_impl_(story_provider_impl),
       session_storage_(session_storage),
       story_storage_(story_storage),
+      story_mutator_(std::move(story_mutator)),
+      story_observer_(std::move(story_observer)),
       story_visibility_system_(story_visibility_system),
-      story_shell_context_impl_{story_id, story_provider_impl, this},
+      story_shell_context_impl_{story_id_, story_provider_impl, this},
       weak_factory_(this) {
   auto story_scope = fuchsia::modular::StoryScope::New();
-  story_scope->story_id = story_id;
+  story_scope->story_id = story_id_;
   auto scope = fuchsia::modular::ComponentScope::New();
   scope->set_story_scope(std::move(*story_scope));
   story_provider_impl_->user_intelligence_provider()
@@ -1207,6 +1211,11 @@
       [this](fuchsia::modular::ModuleData module_data) {
         OnModuleDataUpdated(std::move(module_data));
       });
+
+  story_observer_->RegisterListener(
+      [this](const fuchsia::modular::storymodel::StoryModel& model) {
+        NotifyStoryWatchers(model);
+      });
 }
 
 StoryControllerImpl::~StoryControllerImpl() = default;
@@ -1217,7 +1226,7 @@
 }
 
 bool StoryControllerImpl::IsRunning() {
-  switch (state_) {
+  switch (*story_observer_->model().runtime_state()) {
     case fuchsia::modular::StoryState::RUNNING:
       return true;
     case fuchsia::modular::StoryState::STOPPING:
@@ -1226,10 +1235,6 @@
   }
 }
 
-fuchsia::modular::StoryState StoryControllerImpl::GetStoryState() const {
-  return state_;
-}
-
 fidl::VectorPtr<fuchsia::modular::OngoingActivityType>
 StoryControllerImpl::GetOngoingActivities() {
   fidl::VectorPtr<fuchsia::modular::OngoingActivityType> ongoing_activities;
@@ -1274,7 +1279,9 @@
   running_mod_infos_.erase(fit);
 }
 
-fidl::StringPtr StoryControllerImpl::GetStoryId() const { return story_id_; }
+fidl::StringPtr StoryControllerImpl::GetStoryId() const {
+  return *story_observer_->model().name();
+}
 
 void StoryControllerImpl::RequestStoryFocus() {
   story_provider_impl_->RequestStoryFocus(story_id_);
@@ -1455,12 +1462,12 @@
   // Synced such that if GetInfo() is called after Start() or Stop(), the
   // state after the previously invoked operation is returned.
   //
-  // If this call enters a race with a StoryProvider.DeleteStory() call, resulting in |this|
-  // being destroyed, |callback| will be dropped.
+  // If this call enters a race with a StoryProvider.DeleteStory() call,
+  // resulting in |this| being destroyed, |callback| will be dropped.
   operation_queue_.Add(new SyncCall([this, callback] {
     auto story_info = story_provider_impl_->GetCachedStoryInfo(story_id_);
     FXL_CHECK(story_info);
-    callback(std::move(*story_info), state_);
+    callback(std::move(*story_info), *story_observer_->model().runtime_state());
   }));
 }
 
@@ -1495,7 +1502,7 @@
 void StoryControllerImpl::Watch(
     fidl::InterfaceHandle<fuchsia::modular::StoryWatcher> watcher) {
   auto ptr = watcher.Bind();
-  ptr->OnStateChange(state_);
+  NotifyOneStoryWatcher(story_observer_->model(), ptr.get());
   watchers_.AddInterfacePtr(std::move(ptr));
 }
 
@@ -1601,19 +1608,22 @@
   }
 }
 
-void StoryControllerImpl::SetState(
+void StoryControllerImpl::SetRuntimeState(
     const fuchsia::modular::StoryState new_state) {
-  if (new_state == state_) {
-    return;
-  }
+  story_mutator_->set_runtime_state(new_state);
+}
 
-  state_ = new_state;
-
+void StoryControllerImpl::NotifyStoryWatchers(
+    const fuchsia::modular::storymodel::StoryModel& model) {
   for (auto& i : watchers_.ptrs()) {
-    (*i)->OnStateChange(state_);
+    NotifyOneStoryWatcher(model, (*i).get());
   }
+}
 
-  story_provider_impl_->NotifyStoryStateChange(story_id_);
+void StoryControllerImpl::NotifyOneStoryWatcher(
+    const fuchsia::modular::storymodel::StoryModel& model,
+    fuchsia::modular::StoryWatcher* watcher) {
+  watcher->OnStateChange(*model.runtime_state());
 }
 
 bool StoryControllerImpl::IsExternalModule(
diff --git a/bin/sessionmgr/story_runner/story_controller_impl.h b/bin/sessionmgr/story_runner/story_controller_impl.h
index 5c9210c..6e517fe 100644
--- a/bin/sessionmgr/story_runner/story_controller_impl.h
+++ b/bin/sessionmgr/story_runner/story_controller_impl.h
@@ -31,6 +31,8 @@
 #include <lib/fxl/macros.h>
 
 #include "peridot/bin/sessionmgr/storage/session_storage.h"
+#include "peridot/bin/sessionmgr/story/model/story_observer.h"
+#include "peridot/bin/sessionmgr/story/model/story_mutator.h"
 #include "peridot/bin/sessionmgr/story_runner/link_impl.h"
 #include "peridot/bin/sessionmgr/story_runner/ongoing_activity_impl.h"
 #include "peridot/bin/sessionmgr/story_runner/story_shell_context_impl.h"
@@ -54,8 +56,10 @@
 // clients control over the story.
 class StoryControllerImpl : fuchsia::modular::StoryController {
  public:
-  StoryControllerImpl(fidl::StringPtr story_id, SessionStorage* session_storage,
+  StoryControllerImpl(SessionStorage* session_storage,
                       StoryStorage* story_storage,
+                      std::unique_ptr<StoryMutator> story_mutator,
+                      std::unique_ptr<StoryObserver> story_observer,
                       StoryVisibilitySystem* story_visibility_system,
                       StoryProviderImpl* story_provider_impl);
   ~StoryControllerImpl() override;
@@ -68,9 +72,6 @@
   bool IsRunning();
 
   // Called by StoryProviderImpl.
-  fuchsia::modular::StoryState GetStoryState() const;
-
-  // Called by StoryProviderImpl.
   //
   // Returns a list of the ongoing activities in this story.
   fidl::VectorPtr<fuchsia::modular::OngoingActivityType> GetOngoingActivities();
@@ -93,7 +94,7 @@
   // storage. It is the caller's responsibility to delete |controller|.
   void ReleaseModule(ModuleControllerImpl* module_controller_impl);
 
-  // Called by ModuleContextImpl and StoryProviderImpl.
+  // Called by ModuleContextImpl.
   fidl::StringPtr GetStoryId() const;
 
   // Called by ModuleContextImpl.
@@ -199,8 +200,12 @@
   void OnModuleDataUpdated(fuchsia::modular::ModuleData module_data);
 
   // Misc internal helpers.
-  void SetState(fuchsia::modular::StoryState new_state);
-  void UpdateStoryState(fuchsia::modular::ModuleState state);
+  void SetRuntimeState(fuchsia::modular::StoryState new_state);
+  void NotifyStoryWatchers(
+      const fuchsia::modular::storymodel::StoryModel& model);
+  void NotifyOneStoryWatcher(
+      const fuchsia::modular::storymodel::StoryModel& model,
+      fuchsia::modular::StoryWatcher* watcher);
   void ProcessPendingViews();
   std::set<fuchsia::modular::LinkPath> GetActiveLinksInternal();
 
@@ -219,8 +224,9 @@
   // processes.
   void DestroyStoryEnvironment();
 
-  // The ID of the story, its state and the context to obtain it from and
-  // persist it to.
+  // The ID of the story, copied from |story_observer_| for convenience in
+  // transitioning clients.  TODO(thatguy): Remove users of this in favor of
+  // reading from the |story_observer_| directly.
   const fidl::StringPtr story_id_;
 
   // True once AttachView() was called. Temporarily needed during transition
@@ -228,15 +234,13 @@
   // 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};
-
   StoryProviderImpl* const story_provider_impl_;  // Not owned.
 
   SessionStorage* const session_storage_;  // Not owned.
   StoryStorage* const story_storage_;  // Not owned.
 
+  std::unique_ptr<StoryMutator> story_mutator_;
+  std::unique_ptr<StoryObserver> story_observer_;
   StoryVisibilitySystem* const story_visibility_system_;  // Not owned.
 
   // The application environment (which abstracts a zx::job) in which the
diff --git a/bin/sessionmgr/story_runner/story_provider_impl.cc b/bin/sessionmgr/story_runner/story_provider_impl.cc
index c0aa96b..34f09d1 100644
--- a/bin/sessionmgr/story_runner/story_provider_impl.cc
+++ b/bin/sessionmgr/story_runner/story_provider_impl.cc
@@ -166,7 +166,9 @@
                       container.model_owner->NewMutator());
 
               container.controller_impl = std::make_unique<StoryControllerImpl>(
-                  story_id_, session_storage_, container.storage.get(),
+                  session_storage_, container.storage.get(),
+                  container.model_owner->NewMutator(),
+                  container.model_owner->NewObserver(),
                   story_visibility_system.get(), story_provider_impl_);
               container.entity_provider = std::make_unique<StoryEntityProvider>(
                   container.storage.get());
@@ -400,7 +402,7 @@
     const auto& container = item.second;
     watcher_ptr->OnChange(
         CloneStruct(container.current_data->story_info),
-        container.controller_impl->GetStoryState(),
+        *container.model_observer->model().runtime_state(),
         *container.model_observer->model().visibility_state());
   }
   watchers_.AddInterfacePtr(std::move(watcher_ptr));
@@ -413,7 +415,7 @@
   for (const auto& item : story_runtime_containers_) {
     const auto& container = item.second;
     watcher_ptr->OnStoryActivityChange(
-        container.controller_impl->GetStoryId(),
+        *container.model_observer->model().name(),
         container.controller_impl->GetOngoingActivities());
   }
   activity_watchers_.AddInterfacePtr(std::move(watcher_ptr));
@@ -534,7 +536,7 @@
     return;
   }
   NotifyStoryWatchers(it->second.current_data.get(),
-                      it->second.controller_impl->GetStoryState(),
+                      *it->second.model_observer->model().runtime_state(),
                       *it->second.model_observer->model().visibility_state());
 }
 
@@ -622,16 +624,16 @@
   // StoryData and get runtime state available from it.
   //
   // Otherwise, use defaults for an unloaded story.
-  fuchsia::modular::StoryState state = fuchsia::modular::StoryState::STOPPED;
+  fuchsia::modular::StoryState runtime_state = fuchsia::modular::StoryState::STOPPED;
   fuchsia::modular::StoryVisibilityState visibility_state =
       fuchsia::modular::StoryVisibilityState::DEFAULT;
   auto i = story_runtime_containers_.find(story_data.story_info.id);
   if (i != story_runtime_containers_.end()) {
-    state = i->second.controller_impl->GetStoryState();
+    runtime_state = *i->second.model_observer->model().runtime_state();
     visibility_state = *i->second.model_observer->model().visibility_state();
     i->second.current_data = CloneOptional(story_data);
   }
-  NotifyStoryWatchers(&story_data, state, visibility_state);
+  NotifyStoryWatchers(&story_data, runtime_state, visibility_state);
 }
 
 void StoryProviderImpl::OnStoryStorageDeleted(fidl::StringPtr story_id) {
diff --git a/bin/sessionmgr/story_runner/story_provider_impl.h b/bin/sessionmgr/story_runner/story_provider_impl.h
index dcb535e..2da631e 100644
--- a/bin/sessionmgr/story_runner/story_provider_impl.h
+++ b/bin/sessionmgr/story_runner/story_provider_impl.h
@@ -28,9 +28,9 @@
 #include "peridot/bin/sessionmgr/agent_runner/agent_runner.h"
 #include "peridot/bin/sessionmgr/component_context_impl.h"
 #include "peridot/bin/sessionmgr/message_queue/message_queue_manager.h"
-#include "peridot/bin/sessionmgr/story/system.h"
-#include "peridot/bin/sessionmgr/story/model/story_model_owner.h"
 #include "peridot/bin/sessionmgr/story/model/noop_story_model_storage.h"
+#include "peridot/bin/sessionmgr/story/model/story_model_owner.h"
+#include "peridot/bin/sessionmgr/story/system.h"
 #include "peridot/bin/sessionmgr/story_runner/story_entity_provider.h"
 #include "peridot/lib/fidl/app_client.h"
 #include "peridot/lib/fidl/environment.h"
@@ -144,9 +144,6 @@
   void DetachView(fidl::StringPtr story_id, std::function<void()> done);
 
   // Called by StoryControllerImpl.
-  void NotifyStoryStateChange(fidl::StringPtr story_id);
-
-  // Called by StoryControllerImpl.
   void NotifyStoryActivityChange(
       fidl::StringPtr story_id,
       fidl::VectorPtr<fuchsia::modular::OngoingActivityType>
@@ -227,6 +224,10 @@
   void OnStoryStorageUpdated(fidl::StringPtr story_id,
                              fuchsia::modular::internal::StoryData story_data);
 
+  // Called indirectly through observation of loaded StoryModels. Calls
+  // NotifyStoryWatchers().
+  void NotifyStoryStateChange(fidl::StringPtr story_id);
+
   void NotifyStoryWatchers(
       const fuchsia::modular::internal::StoryData* story_data,
       fuchsia::modular::StoryState story_state,
diff --git a/public/fidl/fuchsia.modular.storymodel/story_model.fidl b/public/fidl/fuchsia.modular.storymodel/story_model.fidl
index 8bf437d..a496a22 100644
--- a/public/fidl/fuchsia.modular.storymodel/story_model.fidl
+++ b/public/fidl/fuchsia.modular.storymodel/story_model.fidl
@@ -17,16 +17,21 @@
   // Always set. Immutable.
   1: string:MAX_STORY_NAME_LENGTH name;
 
+  // An enum describing if the story is RUNNING, STOPPING, STOPPED.
+  //
+  // Always set. Defaults to StoryState::STOPPED.
+  2: fuchsia.modular.StoryState runtime_state;
+
   // An enum describing how the story should be displayed, when focused,
   // in the StoryShell.
   //
   // Always set. Defaults to StoryVisibilityState::DEFAULT.
-  2: fuchsia.modular.StoryVisibilityState visibility_state;
+  3: fuchsia.modular.StoryVisibilityState visibility_state;
 
   // A list of modules present in the story.
   //
   // Always set. Defaults to an empty list.
-  3: vector<ModuleModel>:MAX_MODULES_PER_STORY modules;
+  4: vector<ModuleModel>:MAX_MODULES_PER_STORY modules;
 };
 
 table ModuleModel {
diff --git a/public/fidl/fuchsia.modular.storymodel/story_model_mutation.fidl b/public/fidl/fuchsia.modular.storymodel/story_model_mutation.fidl
index 05d6f15..9d096e6 100644
--- a/public/fidl/fuchsia.modular.storymodel/story_model_mutation.fidl
+++ b/public/fidl/fuchsia.modular.storymodel/story_model_mutation.fidl
@@ -16,4 +16,7 @@
 union StoryModelMutation {
   // Sets the value of |StoryModel.visibility_state|.
   fuchsia.modular.StoryVisibilityState set_visibility_state;
+
+  // Sets the value of |StoryModel.runtime_state|.
+  fuchsia.modular.StoryState set_runtime_state;
 };