blob: 169f6bd132c125cd1d01138ec14dca9489ec2297 [file] [log] [blame]
Yifan Hongdad0af82020-02-19 17:19:49 -08001//
2// Copyright (C) 2020 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#include "update_engine/cleanup_previous_update_action.h"
17
18#include <functional>
19#include <string>
20
21#include <android-base/properties.h>
22#include <base/bind.h>
23
24#include "update_engine/common/utils.h"
25#include "update_engine/payload_consumer/delta_performer.h"
26
27using android::snapshot::SnapshotManager;
28using android::snapshot::UpdateState;
29using brillo::MessageLoop;
30
31constexpr char kBootCompletedProp[] = "sys.boot_completed";
32// Interval to check sys.boot_completed.
33constexpr auto kCheckBootCompletedInterval = base::TimeDelta::FromSeconds(2);
34// Interval to check IBootControl::isSlotMarkedSuccessful
35constexpr auto kCheckSlotMarkedSuccessfulInterval =
36 base::TimeDelta::FromSeconds(2);
37// Interval to call SnapshotManager::ProcessUpdateState
38constexpr auto kWaitForMergeInterval = base::TimeDelta::FromSeconds(2);
39
40namespace chromeos_update_engine {
41
42CleanupPreviousUpdateAction::CleanupPreviousUpdateAction(
43 PrefsInterface* prefs,
44 BootControlInterface* boot_control,
45 android::snapshot::SnapshotManager* snapshot,
46 CleanupPreviousUpdateActionDelegateInterface* delegate)
47 : prefs_(prefs),
48 boot_control_(boot_control),
49 snapshot_(snapshot),
50 delegate_(delegate),
51 running_(false),
52 cancel_failed_(false),
53 last_percentage_(0) {}
54
55void CleanupPreviousUpdateAction::PerformAction() {
56 ResumeAction();
57}
58
59void CleanupPreviousUpdateAction::TerminateProcessing() {
60 SuspendAction();
61}
62
63void CleanupPreviousUpdateAction::ResumeAction() {
64 CHECK(prefs_);
65 CHECK(boot_control_);
66
67 LOG(INFO) << "Starting/resuming CleanupPreviousUpdateAction";
68 running_ = true;
69 StartActionInternal();
70}
71
72void CleanupPreviousUpdateAction::SuspendAction() {
73 LOG(INFO) << "Stopping/suspending CleanupPreviousUpdateAction";
74 running_ = false;
75}
76
77void CleanupPreviousUpdateAction::ActionCompleted(ErrorCode error_code) {
78 running_ = false;
79}
80
81std::string CleanupPreviousUpdateAction::Type() const {
82 return StaticType();
83}
84
85std::string CleanupPreviousUpdateAction::StaticType() {
86 return "CleanupPreviousUpdateAction";
87}
88
89void CleanupPreviousUpdateAction::StartActionInternal() {
90 // Do nothing on non-VAB device.
91 if (!boot_control_->GetDynamicPartitionControl()
92 ->GetVirtualAbFeatureFlag()
93 .IsEnabled()) {
94 processor_->ActionComplete(this, ErrorCode::kSuccess);
95 return;
96 }
97 // SnapshotManager is only available on VAB devices.
98 CHECK(snapshot_);
99 WaitBootCompletedOrSchedule();
100}
101
102void CleanupPreviousUpdateAction::ScheduleWaitBootCompleted() {
103 TEST_AND_RETURN(running_);
104 MessageLoop::current()->PostDelayedTask(
105 FROM_HERE,
106 base::Bind(&CleanupPreviousUpdateAction::WaitBootCompletedOrSchedule,
107 base::Unretained(this)),
108 kCheckBootCompletedInterval);
109}
110
111void CleanupPreviousUpdateAction::WaitBootCompletedOrSchedule() {
112 TEST_AND_RETURN(running_);
113 if (!android::base::GetBoolProperty(kBootCompletedProp, false)) {
114 // repeat
115 ScheduleWaitBootCompleted();
116 return;
117 }
118
119 LOG(INFO) << "Boot completed, waiting on markBootSuccessful()";
120 CheckSlotMarkedSuccessfulOrSchedule();
121}
122
123void CleanupPreviousUpdateAction::ScheduleWaitMarkBootSuccessful() {
124 TEST_AND_RETURN(running_);
125 MessageLoop::current()->PostDelayedTask(
126 FROM_HERE,
127 base::Bind(
128 &CleanupPreviousUpdateAction::CheckSlotMarkedSuccessfulOrSchedule,
129 base::Unretained(this)),
130 kCheckSlotMarkedSuccessfulInterval);
131}
132
133void CleanupPreviousUpdateAction::CheckSlotMarkedSuccessfulOrSchedule() {
134 TEST_AND_RETURN(running_);
135 if (!boot_control_->IsSlotMarkedSuccessful(boot_control_->GetCurrentSlot())) {
136 ScheduleWaitMarkBootSuccessful();
137 }
138 LOG(INFO) << "Waiting for any previous merge request to complete. "
139 << "This can take up to several minutes.";
140 WaitForMergeOrSchedule();
141}
142
143void CleanupPreviousUpdateAction::ScheduleWaitForMerge() {
144 TEST_AND_RETURN(running_);
145 MessageLoop::current()->PostDelayedTask(
146 FROM_HERE,
147 base::Bind(&CleanupPreviousUpdateAction::WaitForMergeOrSchedule,
148 base::Unretained(this)),
149 kWaitForMergeInterval);
150}
151
152void CleanupPreviousUpdateAction::WaitForMergeOrSchedule() {
153 TEST_AND_RETURN(running_);
154 auto state = snapshot_->ProcessUpdateState(
155 std::bind(&CleanupPreviousUpdateAction::OnMergePercentageUpdate, this),
156 std::bind(&CleanupPreviousUpdateAction::BeforeCancel, this));
157
158 // TODO(elsk): log stats
159 switch (state) {
160 case UpdateState::None: {
161 LOG(INFO) << "Can't find any snapshot to merge.";
162 processor_->ActionComplete(this, ErrorCode::kSuccess);
163 return;
164 }
165
166 case UpdateState::Initiated: {
167 LOG(ERROR) << "Previous update has not been completed, not cleaning up";
168 processor_->ActionComplete(this, ErrorCode::kSuccess);
169 return;
170 }
171
172 case UpdateState::Unverified: {
173 InitiateMergeAndWait();
174 return;
175 }
176
177 case UpdateState::Merging: {
178 ScheduleWaitForMerge();
179 return;
180 }
181
182 case UpdateState::MergeNeedsReboot: {
183 LOG(ERROR) << "Need reboot to finish merging.";
184 processor_->ActionComplete(this, ErrorCode::kError);
185 return;
186 }
187
188 case UpdateState::MergeCompleted: {
189 LOG(INFO) << "Merge finished with state MergeCompleted.";
190 processor_->ActionComplete(this, ErrorCode::kSuccess);
191 return;
192 }
193
194 case UpdateState::MergeFailed: {
195 LOG(ERROR) << "Merge failed. Device may be corrupted.";
196 processor_->ActionComplete(this, ErrorCode::kDeviceCorrupted);
197 return;
198 }
199
200 case UpdateState::Cancelled: {
201 // DeltaPerformer::ResetUpdateProgress failed, hence snapshots are
202 // not deleted to avoid inconsistency.
203 // Nothing can be done here; just try next time.
204 ErrorCode error_code =
205 cancel_failed_ ? ErrorCode::kError : ErrorCode::kSuccess;
206 processor_->ActionComplete(this, error_code);
207 return;
208 }
209
210 default: {
211 // Protobuf has some reserved enum values, so a default case is needed.
212 LOG(FATAL) << "SnapshotManager::ProcessUpdateState returns "
213 << static_cast<int32_t>(state);
214 }
215 }
216}
217
218bool CleanupPreviousUpdateAction::OnMergePercentageUpdate() {
219 double percentage = 0.0;
220 snapshot_->GetUpdateState(&percentage);
221 if (delegate_) {
222 // libsnapshot uses [0, 100] percentage but update_engine uses [0, 1].
223 delegate_->OnCleanupProgressUpdate(percentage / 100);
224 }
225
226 // Log if percentage increments by at least 1.
227 if (last_percentage_ < static_cast<unsigned int>(percentage)) {
228 last_percentage_ = percentage;
229 LOG(INFO) << "Waiting for merge to complete: " << last_percentage_ << "%.";
230 }
231
232 // Do not continue to wait for merge. Instead, let ProcessUpdateState
233 // return Merging directly so that we can ScheduleWaitForMerge() in
234 // MessageLoop.
235 return false;
236}
237
238bool CleanupPreviousUpdateAction::BeforeCancel() {
239 if (DeltaPerformer::ResetUpdateProgress(
240 prefs_,
241 false /* quick */,
242 false /* skip dynamic partitions metadata*/)) {
243 return true;
244 }
245
246 // ResetUpdateProgress might not work on stub prefs. Do additional checks.
247 LOG(WARNING) << "ProcessUpdateState returns Cancelled but cleanup failed.";
248
249 std::string val;
250 ignore_result(prefs_->GetString(kPrefsDynamicPartitionMetadataUpdated, &val));
251 if (val.empty()) {
252 LOG(INFO) << kPrefsDynamicPartitionMetadataUpdated
253 << " is empty, assuming successful cleanup";
254 return true;
255 }
256 LOG(WARNING)
257 << kPrefsDynamicPartitionMetadataUpdated << " is " << val
258 << ", not deleting snapshots even though UpdateState is Cancelled.";
259 cancel_failed_ = true;
260 return false;
261}
262
263void CleanupPreviousUpdateAction::InitiateMergeAndWait() {
264 TEST_AND_RETURN(running_);
265 LOG(INFO) << "Attempting to initiate merge.";
266
267 if (snapshot_->InitiateMerge()) {
268 WaitForMergeOrSchedule();
269 return;
270 }
271
272 LOG(WARNING) << "InitiateMerge failed.";
273 auto state = snapshot_->GetUpdateState();
274 if (state == UpdateState::Unverified) {
275 // We are stuck at unverified state. This can happen if the update has
276 // been applied, but it has not even been attempted yet (in libsnapshot,
277 // rollback indicator does not exist); for example, if update_engine
278 // restarts before the device reboots, then this state may be reached.
279 // Nothing should be done here.
280 LOG(WARNING) << "InitiateMerge leaves the device at "
281 << "UpdateState::Unverified. (Did update_engine "
282 << "restarted?)";
283 processor_->ActionComplete(this, ErrorCode::kSuccess);
284 return;
285 }
286
287 // State does seems to be advanced.
288 // It is possibly racy. For example, on a userdebug build, the user may
289 // manually initiate a merge with snapshotctl between last time
290 // update_engine checks UpdateState. Hence, just call
291 // WaitForMergeOrSchedule one more time.
292 LOG(WARNING) << "IniitateMerge failed but GetUpdateState returned "
293 << android::snapshot::UpdateState_Name(state)
294 << ", try to wait for merge again.";
295 WaitForMergeOrSchedule();
296 return;
297}
298
299} // namespace chromeos_update_engine