Allow to Suspend/Resume the ActionProcessor.

This patch implements the core functionality of suspend/resume actions
from the ActionProcessor. No actions support suspend/resume yet.

Bug: 27047026
TEST=Added unittets, tested on edison-eng.

Change-Id: Ib9600098dbccf05fc30f10f0add4a5bc87892b66
diff --git a/common/action_processor.cc b/common/action_processor.cc
index 7ccdfbd..2618e4e 100644
--- a/common/action_processor.cc
+++ b/common/action_processor.cc
@@ -21,14 +21,12 @@
 #include <base/logging.h>
 
 #include "update_engine/common/action.h"
+#include "update_engine/common/error_code_utils.h"
 
 using std::string;
 
 namespace chromeos_update_engine {
 
-ActionProcessor::ActionProcessor()
-    : current_action_(nullptr), delegate_(nullptr) {}
-
 ActionProcessor::~ActionProcessor() {
   if (IsRunning())
     StopProcessing();
@@ -45,8 +43,7 @@
   CHECK(!IsRunning());
   if (!actions_.empty()) {
     current_action_ = actions_.front();
-    LOG(INFO) << "ActionProcessor::StartProcessing: "
-              << current_action_->Type();
+    LOG(INFO) << "ActionProcessor: starting " << current_action_->Type();
     actions_.pop_front();
     current_action_->PerformAction();
   }
@@ -54,17 +51,55 @@
 
 void ActionProcessor::StopProcessing() {
   CHECK(IsRunning());
-  CHECK(current_action_);
-  current_action_->TerminateProcessing();
-  CHECK(current_action_);
-  current_action_->SetProcessor(nullptr);
-  LOG(INFO) << "ActionProcessor::StopProcessing: aborted "
-            << current_action_->Type();
+  if (current_action_) {
+    current_action_->TerminateProcessing();
+    current_action_->SetProcessor(nullptr);
+  }
+  LOG(INFO) << "ActionProcessor: aborted "
+            << (current_action_ ? current_action_->Type() : "")
+            << (suspended_ ? " while suspended" : "");
   current_action_ = nullptr;
+  suspended_ = false;
   if (delegate_)
     delegate_->ProcessingStopped(this);
 }
 
+void ActionProcessor::SuspendProcessing() {
+  // No current_action_ when not suspended means that the action processor was
+  // never started or already finished.
+  if (suspended_ || !current_action_) {
+    LOG(WARNING) << "Called SuspendProcessing while not processing.";
+    return;
+  }
+  suspended_ = true;
+
+  // If there's a current action we should notify it that it should suspend, but
+  // the action can ignore that and terminate at any point.
+  LOG(INFO) << "ActionProcessor: suspending " << current_action_->Type();
+  current_action_->SuspendAction();
+}
+
+void ActionProcessor::ResumeProcessing() {
+  if (!suspended_) {
+    LOG(WARNING) << "Called ResumeProcessing while not suspended.";
+    return;
+  }
+  suspended_ = false;
+  if (current_action_) {
+    // The current_action_ did not call ActionComplete while suspended, so we
+    // should notify it of the resume operation.
+    LOG(INFO) << "ActionProcessor: resuming " << current_action_->Type();
+    current_action_->ResumeAction();
+  } else {
+    // The last action called ActionComplete while suspended, so there is
+    // already a log message with the type of the finished action. We simply
+    // state that we are resuming processing and the next function will log the
+    // start of the next action or processing completion.
+    LOG(INFO) << "ActionProcessor: resuming processing";
+    StartNextActionOrFinish(suspended_error_code_);
+  }
+}
+
 void ActionProcessor::ActionComplete(AbstractAction* actionptr,
                                      ErrorCode code) {
   CHECK_EQ(actionptr, current_action_);
@@ -74,17 +109,26 @@
   current_action_->ActionCompleted(code);
   current_action_->SetProcessor(nullptr);
   current_action_ = nullptr;
-  if (actions_.empty()) {
-    LOG(INFO) << "ActionProcessor::ActionComplete: finished last action of"
-                 " type " << old_type;
-  } else if (code != ErrorCode::kSuccess) {
-    LOG(INFO) << "ActionProcessor::ActionComplete: " << old_type
-              << " action failed. Aborting processing.";
+  LOG(INFO) << "ActionProcessor: finished "
+            << (actions_.empty() ? "last action " : "") << old_type
+            << (suspended_ ? " while suspended" : "")
+            << " with code " << utils::ErrorCodeToString(code);
+  if (!actions_.empty() && code != ErrorCode::kSuccess) {
+    LOG(INFO) << "ActionProcessor: Aborting processing due to failure.";
     actions_.clear();
   }
+  if (suspended_) {
+    // If an action finished while suspended we don't start the next action (or
+    // terminate the processing) until the processor is resumed. This condition
+    // will be flagged by a nullptr current_action_ while suspended_ is true.
+    suspended_error_code_ = code;
+    return;
+  }
+  StartNextActionOrFinish(code);
+}
+
+void ActionProcessor::StartNextActionOrFinish(ErrorCode code) {
   if (actions_.empty()) {
-    LOG(INFO) << "ActionProcessor::ActionComplete: finished last action of"
-                 " type " << old_type;
     if (delegate_) {
       delegate_->ProcessingDone(this, code);
     }
@@ -92,8 +136,7 @@
   }
   current_action_ = actions_.front();
   actions_.pop_front();
-  LOG(INFO) << "ActionProcessor::ActionComplete: finished " << old_type
-            << ", starting " << current_action_->Type();
+  LOG(INFO) << "ActionProcessor: starting " << current_action_->Type();
   current_action_->PerformAction();
 }