Add VirtualMachineState enum rather than isRunning bool.

Keep track of payload state and enforce lifecycle expectations.

Bug: 199127239
Test: atest VirtualizationTestCases
Change-Id: I04646c96ec3c15854b6ae705e40979d0f2e29457
diff --git a/virtualizationservice/src/crosvm.rs b/virtualizationservice/src/crosvm.rs
index 5984ff0..abaa955 100644
--- a/virtualizationservice/src/crosvm.rs
+++ b/virtualizationservice/src/crosvm.rs
@@ -52,6 +52,18 @@
     pub writable: bool,
 }
 
+/// The lifecycle state which the payload in the VM has reported itself to be in.
+///
+/// Note that the order of enum variants is significant; only forward transitions are allowed by
+/// [`VmInstance::update_payload_state`].
+#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
+pub enum PayloadState {
+    Starting,
+    Started,
+    Ready,
+    Finished,
+}
+
 /// Information about a particular instance of a VM which is running.
 #[derive(Debug)]
 pub struct VmInstance {
@@ -76,6 +88,8 @@
     pub callbacks: VirtualMachineCallbacks,
     /// Input/output stream of the payload run in the VM.
     pub stream: Mutex<Option<VsockStream>>,
+    /// The latest lifecycle state which the payload reported itself to be in.
+    payload_state: Mutex<PayloadState>,
 }
 
 impl VmInstance {
@@ -100,6 +114,7 @@
             running: AtomicBool::new(true),
             callbacks: Default::default(),
             stream: Mutex::new(None),
+            payload_state: Mutex::new(PayloadState::Starting),
         }
     }
 
@@ -154,6 +169,24 @@
         self.running.load(Ordering::Acquire)
     }
 
+    /// Returns the last reported state of the VM payload.
+    pub fn payload_state(&self) -> PayloadState {
+        *self.payload_state.lock().unwrap()
+    }
+
+    /// Updates the payload state to the given value, if it is a valid state transition.
+    pub fn update_payload_state(&self, new_state: PayloadState) -> Result<(), Error> {
+        let mut state_locked = self.payload_state.lock().unwrap();
+        // Only allow forward transitions, e.g. from starting to started or finished, not back in
+        // the other direction.
+        if new_state > *state_locked {
+            *state_locked = new_state;
+            Ok(())
+        } else {
+            bail!("Invalid payload state transition from {:?} to {:?}", *state_locked, new_state)
+        }
+    }
+
     /// Kill the crosvm instance.
     pub fn kill(&self) {
         // TODO: Talk to crosvm to shutdown cleanly.