blob: 6c5cd1cb8c40cc2912cf0982e6892db1ee429454 [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
Yifan Hongd976cc52020-02-25 14:51:42 -080018#include <chrono> // NOLINT(build/c++11) -- for merge times
Yifan Hongdad0af82020-02-19 17:19:49 -080019#include <functional>
20#include <string>
21
22#include <android-base/properties.h>
23#include <base/bind.h>
24
Yifan Hongd976cc52020-02-25 14:51:42 -080025#ifndef __ANDROID_RECOVERY__
26#include <statslog.h>
27#endif
28
Yifan Hongdad0af82020-02-19 17:19:49 -080029#include "update_engine/common/utils.h"
30#include "update_engine/payload_consumer/delta_performer.h"
31
32using android::snapshot::SnapshotManager;
Yifan Hongd976cc52020-02-25 14:51:42 -080033using android::snapshot::SnapshotMergeStats;
Yifan Hongdad0af82020-02-19 17:19:49 -080034using android::snapshot::UpdateState;
35using brillo::MessageLoop;
36
37constexpr char kBootCompletedProp[] = "sys.boot_completed";
38// Interval to check sys.boot_completed.
39constexpr auto kCheckBootCompletedInterval = base::TimeDelta::FromSeconds(2);
40// Interval to check IBootControl::isSlotMarkedSuccessful
41constexpr auto kCheckSlotMarkedSuccessfulInterval =
42 base::TimeDelta::FromSeconds(2);
43// Interval to call SnapshotManager::ProcessUpdateState
44constexpr auto kWaitForMergeInterval = base::TimeDelta::FromSeconds(2);
45
46namespace chromeos_update_engine {
47
48CleanupPreviousUpdateAction::CleanupPreviousUpdateAction(
49 PrefsInterface* prefs,
50 BootControlInterface* boot_control,
51 android::snapshot::SnapshotManager* snapshot,
52 CleanupPreviousUpdateActionDelegateInterface* delegate)
53 : prefs_(prefs),
54 boot_control_(boot_control),
55 snapshot_(snapshot),
56 delegate_(delegate),
57 running_(false),
58 cancel_failed_(false),
Yifan Hongd976cc52020-02-25 14:51:42 -080059 last_percentage_(0),
60 merge_stats_(SnapshotMergeStats::GetInstance(*snapshot)) {}
Yifan Hongdad0af82020-02-19 17:19:49 -080061
62void CleanupPreviousUpdateAction::PerformAction() {
63 ResumeAction();
64}
65
66void CleanupPreviousUpdateAction::TerminateProcessing() {
67 SuspendAction();
68}
69
70void CleanupPreviousUpdateAction::ResumeAction() {
71 CHECK(prefs_);
72 CHECK(boot_control_);
73
74 LOG(INFO) << "Starting/resuming CleanupPreviousUpdateAction";
75 running_ = true;
76 StartActionInternal();
77}
78
79void CleanupPreviousUpdateAction::SuspendAction() {
80 LOG(INFO) << "Stopping/suspending CleanupPreviousUpdateAction";
81 running_ = false;
82}
83
84void CleanupPreviousUpdateAction::ActionCompleted(ErrorCode error_code) {
85 running_ = false;
Yifan Hongd976cc52020-02-25 14:51:42 -080086 ReportMergeStats();
Yifan Hongdad0af82020-02-19 17:19:49 -080087}
88
89std::string CleanupPreviousUpdateAction::Type() const {
90 return StaticType();
91}
92
93std::string CleanupPreviousUpdateAction::StaticType() {
94 return "CleanupPreviousUpdateAction";
95}
96
97void CleanupPreviousUpdateAction::StartActionInternal() {
98 // Do nothing on non-VAB device.
99 if (!boot_control_->GetDynamicPartitionControl()
100 ->GetVirtualAbFeatureFlag()
101 .IsEnabled()) {
102 processor_->ActionComplete(this, ErrorCode::kSuccess);
103 return;
104 }
105 // SnapshotManager is only available on VAB devices.
106 CHECK(snapshot_);
107 WaitBootCompletedOrSchedule();
108}
109
110void CleanupPreviousUpdateAction::ScheduleWaitBootCompleted() {
111 TEST_AND_RETURN(running_);
112 MessageLoop::current()->PostDelayedTask(
113 FROM_HERE,
114 base::Bind(&CleanupPreviousUpdateAction::WaitBootCompletedOrSchedule,
115 base::Unretained(this)),
116 kCheckBootCompletedInterval);
117}
118
119void CleanupPreviousUpdateAction::WaitBootCompletedOrSchedule() {
120 TEST_AND_RETURN(running_);
121 if (!android::base::GetBoolProperty(kBootCompletedProp, false)) {
122 // repeat
123 ScheduleWaitBootCompleted();
124 return;
125 }
126
127 LOG(INFO) << "Boot completed, waiting on markBootSuccessful()";
128 CheckSlotMarkedSuccessfulOrSchedule();
129}
130
131void CleanupPreviousUpdateAction::ScheduleWaitMarkBootSuccessful() {
132 TEST_AND_RETURN(running_);
133 MessageLoop::current()->PostDelayedTask(
134 FROM_HERE,
135 base::Bind(
136 &CleanupPreviousUpdateAction::CheckSlotMarkedSuccessfulOrSchedule,
137 base::Unretained(this)),
138 kCheckSlotMarkedSuccessfulInterval);
139}
140
141void CleanupPreviousUpdateAction::CheckSlotMarkedSuccessfulOrSchedule() {
142 TEST_AND_RETURN(running_);
143 if (!boot_control_->IsSlotMarkedSuccessful(boot_control_->GetCurrentSlot())) {
144 ScheduleWaitMarkBootSuccessful();
145 }
Yifan Hongd976cc52020-02-25 14:51:42 -0800146 if (!merge_stats_->Start()) {
147 // Not an error because CleanupPreviousUpdateAction may be paused and
148 // resumed while kernel continues merging snapshots in the background.
149 LOG(WARNING) << "SnapshotMergeStats::Start failed.";
150 }
Yifan Hongdad0af82020-02-19 17:19:49 -0800151 LOG(INFO) << "Waiting for any previous merge request to complete. "
152 << "This can take up to several minutes.";
153 WaitForMergeOrSchedule();
154}
155
156void CleanupPreviousUpdateAction::ScheduleWaitForMerge() {
157 TEST_AND_RETURN(running_);
158 MessageLoop::current()->PostDelayedTask(
159 FROM_HERE,
160 base::Bind(&CleanupPreviousUpdateAction::WaitForMergeOrSchedule,
161 base::Unretained(this)),
162 kWaitForMergeInterval);
163}
164
165void CleanupPreviousUpdateAction::WaitForMergeOrSchedule() {
166 TEST_AND_RETURN(running_);
167 auto state = snapshot_->ProcessUpdateState(
168 std::bind(&CleanupPreviousUpdateAction::OnMergePercentageUpdate, this),
169 std::bind(&CleanupPreviousUpdateAction::BeforeCancel, this));
Yifan Hongd976cc52020-02-25 14:51:42 -0800170 merge_stats_->set_state(state);
Yifan Hongdad0af82020-02-19 17:19:49 -0800171
Yifan Hongdad0af82020-02-19 17:19:49 -0800172 switch (state) {
173 case UpdateState::None: {
174 LOG(INFO) << "Can't find any snapshot to merge.";
Yifan Hong16b594a2020-03-05 21:02:36 -0800175 ErrorCode error_code = ErrorCode::kSuccess;
176 if (!snapshot_->CancelUpdate()) {
177 error_code = ErrorCode::kError;
178 LOG(INFO) << "Failed to call SnapshotManager::CancelUpdate().";
179 }
180 processor_->ActionComplete(this, error_code);
Yifan Hongdad0af82020-02-19 17:19:49 -0800181 return;
182 }
183
184 case UpdateState::Initiated: {
185 LOG(ERROR) << "Previous update has not been completed, not cleaning up";
186 processor_->ActionComplete(this, ErrorCode::kSuccess);
187 return;
188 }
189
190 case UpdateState::Unverified: {
191 InitiateMergeAndWait();
192 return;
193 }
194
195 case UpdateState::Merging: {
196 ScheduleWaitForMerge();
197 return;
198 }
199
200 case UpdateState::MergeNeedsReboot: {
201 LOG(ERROR) << "Need reboot to finish merging.";
202 processor_->ActionComplete(this, ErrorCode::kError);
203 return;
204 }
205
206 case UpdateState::MergeCompleted: {
207 LOG(INFO) << "Merge finished with state MergeCompleted.";
208 processor_->ActionComplete(this, ErrorCode::kSuccess);
209 return;
210 }
211
212 case UpdateState::MergeFailed: {
213 LOG(ERROR) << "Merge failed. Device may be corrupted.";
214 processor_->ActionComplete(this, ErrorCode::kDeviceCorrupted);
215 return;
216 }
217
218 case UpdateState::Cancelled: {
219 // DeltaPerformer::ResetUpdateProgress failed, hence snapshots are
220 // not deleted to avoid inconsistency.
221 // Nothing can be done here; just try next time.
222 ErrorCode error_code =
223 cancel_failed_ ? ErrorCode::kError : ErrorCode::kSuccess;
224 processor_->ActionComplete(this, error_code);
225 return;
226 }
227
228 default: {
229 // Protobuf has some reserved enum values, so a default case is needed.
230 LOG(FATAL) << "SnapshotManager::ProcessUpdateState returns "
231 << static_cast<int32_t>(state);
232 }
233 }
234}
235
236bool CleanupPreviousUpdateAction::OnMergePercentageUpdate() {
237 double percentage = 0.0;
238 snapshot_->GetUpdateState(&percentage);
239 if (delegate_) {
240 // libsnapshot uses [0, 100] percentage but update_engine uses [0, 1].
241 delegate_->OnCleanupProgressUpdate(percentage / 100);
242 }
243
244 // Log if percentage increments by at least 1.
245 if (last_percentage_ < static_cast<unsigned int>(percentage)) {
246 last_percentage_ = percentage;
247 LOG(INFO) << "Waiting for merge to complete: " << last_percentage_ << "%.";
248 }
249
250 // Do not continue to wait for merge. Instead, let ProcessUpdateState
251 // return Merging directly so that we can ScheduleWaitForMerge() in
252 // MessageLoop.
253 return false;
254}
255
256bool CleanupPreviousUpdateAction::BeforeCancel() {
257 if (DeltaPerformer::ResetUpdateProgress(
258 prefs_,
259 false /* quick */,
260 false /* skip dynamic partitions metadata*/)) {
261 return true;
262 }
263
264 // ResetUpdateProgress might not work on stub prefs. Do additional checks.
265 LOG(WARNING) << "ProcessUpdateState returns Cancelled but cleanup failed.";
266
267 std::string val;
268 ignore_result(prefs_->GetString(kPrefsDynamicPartitionMetadataUpdated, &val));
269 if (val.empty()) {
270 LOG(INFO) << kPrefsDynamicPartitionMetadataUpdated
271 << " is empty, assuming successful cleanup";
272 return true;
273 }
274 LOG(WARNING)
275 << kPrefsDynamicPartitionMetadataUpdated << " is " << val
276 << ", not deleting snapshots even though UpdateState is Cancelled.";
277 cancel_failed_ = true;
278 return false;
279}
280
281void CleanupPreviousUpdateAction::InitiateMergeAndWait() {
282 TEST_AND_RETURN(running_);
283 LOG(INFO) << "Attempting to initiate merge.";
284
285 if (snapshot_->InitiateMerge()) {
286 WaitForMergeOrSchedule();
287 return;
288 }
289
290 LOG(WARNING) << "InitiateMerge failed.";
291 auto state = snapshot_->GetUpdateState();
Yifan Hongd976cc52020-02-25 14:51:42 -0800292 merge_stats_->set_state(state);
Yifan Hongdad0af82020-02-19 17:19:49 -0800293 if (state == UpdateState::Unverified) {
294 // We are stuck at unverified state. This can happen if the update has
295 // been applied, but it has not even been attempted yet (in libsnapshot,
296 // rollback indicator does not exist); for example, if update_engine
297 // restarts before the device reboots, then this state may be reached.
298 // Nothing should be done here.
299 LOG(WARNING) << "InitiateMerge leaves the device at "
300 << "UpdateState::Unverified. (Did update_engine "
301 << "restarted?)";
302 processor_->ActionComplete(this, ErrorCode::kSuccess);
303 return;
304 }
305
306 // State does seems to be advanced.
307 // It is possibly racy. For example, on a userdebug build, the user may
308 // manually initiate a merge with snapshotctl between last time
309 // update_engine checks UpdateState. Hence, just call
310 // WaitForMergeOrSchedule one more time.
311 LOG(WARNING) << "IniitateMerge failed but GetUpdateState returned "
312 << android::snapshot::UpdateState_Name(state)
313 << ", try to wait for merge again.";
314 WaitForMergeOrSchedule();
315 return;
316}
317
Yifan Hongd976cc52020-02-25 14:51:42 -0800318void CleanupPreviousUpdateAction::ReportMergeStats() {
319 auto result = merge_stats_->Finish();
320 if (result == nullptr) {
321 LOG(WARNING) << "Not reporting merge stats because "
322 "SnapshotMergeStats::Finish failed.";
323 return;
324 }
325
326#ifdef __ANDROID_RECOVERY__
327 LOG(INFO) << "Skip reporting merge stats in recovery.";
328#else
329 const auto& report = result->report();
330
331 if (report.state() == UpdateState::None ||
332 report.state() == UpdateState::Initiated ||
333 report.state() == UpdateState::Unverified) {
334 LOG(INFO) << "Not reporting merge stats because state is "
335 << android::snapshot::UpdateState_Name(report.state());
336 return;
337 }
338
339 auto passed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
340 result->merge_time());
341 LOG(INFO) << "Reporting merge stats: "
342 << android::snapshot::UpdateState_Name(report.state()) << " in "
343 << passed_ms.count() << "ms (resumed " << report.resume_count()
344 << " times)";
Yifan Hongbe59a002020-03-02 15:45:14 -0800345 android::util::stats_write(android::util::SNAPSHOT_MERGE_REPORTED,
346 static_cast<int32_t>(report.state()),
347 static_cast<int64_t>(passed_ms.count()),
348 static_cast<int32_t>(report.resume_count()));
Yifan Hongd976cc52020-02-25 14:51:42 -0800349#endif
350}
351
Yifan Hongdad0af82020-02-19 17:19:49 -0800352} // namespace chromeos_update_engine