1//
2// Copyright (C) 2009 The Android Open Source Project
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//      http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15//
16
17#include "update_engine/common/action_processor.h"
18
19#include <string>
20
21#include <base/logging.h>
22
23#include "update_engine/common/action.h"
24#include "update_engine/common/error_code_utils.h"
25
26using std::string;
27
28namespace chromeos_update_engine {
29
30ActionProcessor::~ActionProcessor() {
31  if (IsRunning())
32    StopProcessing();
33  for (auto action : actions_)
34    action->SetProcessor(nullptr);
35}
36
37void ActionProcessor::EnqueueAction(AbstractAction* action) {
38  actions_.push_back(action);
39  action->SetProcessor(this);
40}
41
42void ActionProcessor::StartProcessing() {
43  CHECK(!IsRunning());
44  if (!actions_.empty()) {
45    current_action_ = actions_.front();
46    LOG(INFO) << "ActionProcessor: starting " << current_action_->Type();
47    actions_.pop_front();
48    current_action_->PerformAction();
49  }
50}
51
52void ActionProcessor::StopProcessing() {
53  CHECK(IsRunning());
54  if (current_action_) {
55    current_action_->TerminateProcessing();
56    current_action_->SetProcessor(nullptr);
57  }
58  LOG(INFO) << "ActionProcessor: aborted "
59            << (current_action_ ? current_action_->Type() : "")
60            << (suspended_ ? " while suspended" : "");
61  current_action_ = nullptr;
62  suspended_ = false;
63  // Delete all the actions before calling the delegate.
64  for (auto action : actions_)
65    action->SetProcessor(nullptr);
66  actions_.clear();
67  if (delegate_)
68    delegate_->ProcessingStopped(this);
69}
70
71void ActionProcessor::SuspendProcessing() {
72  // No current_action_ when not suspended means that the action processor was
73  // never started or already finished.
74  if (suspended_ || !current_action_) {
75    LOG(WARNING) << "Called SuspendProcessing while not processing.";
76    return;
77  }
78  suspended_ = true;
79
80  // If there's a current action we should notify it that it should suspend, but
81  // the action can ignore that and terminate at any point.
82  LOG(INFO) << "ActionProcessor: suspending " << current_action_->Type();
83  current_action_->SuspendAction();
84}
85
86void ActionProcessor::ResumeProcessing() {
87  if (!suspended_) {
88    LOG(WARNING) << "Called ResumeProcessing while not suspended.";
89    return;
90  }
91  suspended_ = false;
92  if (current_action_) {
93    // The current_action_ did not call ActionComplete while suspended, so we
94    // should notify it of the resume operation.
95    LOG(INFO) << "ActionProcessor: resuming " << current_action_->Type();
96    current_action_->ResumeAction();
97  } else {
98    // The last action called ActionComplete while suspended, so there is
99    // already a log message with the type of the finished action. We simply
100    // state that we are resuming processing and the next function will log the
101    // start of the next action or processing completion.
102    LOG(INFO) << "ActionProcessor: resuming processing";
103    StartNextActionOrFinish(suspended_error_code_);
104  }
105}
106
107void ActionProcessor::ActionComplete(AbstractAction* actionptr,
108                                     ErrorCode code) {
109  CHECK_EQ(actionptr, current_action_);
110  if (delegate_)
111    delegate_->ActionCompleted(this, actionptr, code);
112  string old_type = current_action_->Type();
113  current_action_->ActionCompleted(code);
114  current_action_->SetProcessor(nullptr);
115  current_action_ = nullptr;
116  LOG(INFO) << "ActionProcessor: finished "
117            << (actions_.empty() ? "last action " : "") << old_type
118            << (suspended_ ? " while suspended" : "")
119            << " with code " << utils::ErrorCodeToString(code);
120  if (!actions_.empty() && code != ErrorCode::kSuccess) {
121    LOG(INFO) << "ActionProcessor: Aborting processing due to failure.";
122    actions_.clear();
123  }
124  if (suspended_) {
125    // If an action finished while suspended we don't start the next action (or
126    // terminate the processing) until the processor is resumed. This condition
127    // will be flagged by a nullptr current_action_ while suspended_ is true.
128    suspended_error_code_ = code;
129    return;
130  }
131  StartNextActionOrFinish(code);
132}
133
134void ActionProcessor::StartNextActionOrFinish(ErrorCode code) {
135  if (actions_.empty()) {
136    if (delegate_) {
137      delegate_->ProcessingDone(this, code);
138    }
139    return;
140  }
141  current_action_ = actions_.front();
142  actions_.pop_front();
143  LOG(INFO) << "ActionProcessor: starting " << current_action_->Type();
144  current_action_->PerformAction();
145}
146
147}  // namespace chromeos_update_engine
148