blob: f6838f9747186fdadcfec59f21089cc615a4e273 [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.";
175 processor_->ActionComplete(this, ErrorCode::kSuccess);
176 return;
177 }
178
179 case UpdateState::Initiated: {
180 LOG(ERROR) << "Previous update has not been completed, not cleaning up";
181 processor_->ActionComplete(this, ErrorCode::kSuccess);
182 return;
183 }
184
185 case UpdateState::Unverified: {
186 InitiateMergeAndWait();
187 return;
188 }
189
190 case UpdateState::Merging: {
191 ScheduleWaitForMerge();
192 return;
193 }
194
195 case UpdateState::MergeNeedsReboot: {
196 LOG(ERROR) << "Need reboot to finish merging.";
197 processor_->ActionComplete(this, ErrorCode::kError);
198 return;
199 }
200
201 case UpdateState::MergeCompleted: {
202 LOG(INFO) << "Merge finished with state MergeCompleted.";
203 processor_->ActionComplete(this, ErrorCode::kSuccess);
204 return;
205 }
206
207 case UpdateState::MergeFailed: {
208 LOG(ERROR) << "Merge failed. Device may be corrupted.";
209 processor_->ActionComplete(this, ErrorCode::kDeviceCorrupted);
210 return;
211 }
212
213 case UpdateState::Cancelled: {
214 // DeltaPerformer::ResetUpdateProgress failed, hence snapshots are
215 // not deleted to avoid inconsistency.
216 // Nothing can be done here; just try next time.
217 ErrorCode error_code =
218 cancel_failed_ ? ErrorCode::kError : ErrorCode::kSuccess;
219 processor_->ActionComplete(this, error_code);
220 return;
221 }
222
223 default: {
224 // Protobuf has some reserved enum values, so a default case is needed.
225 LOG(FATAL) << "SnapshotManager::ProcessUpdateState returns "
226 << static_cast<int32_t>(state);
227 }
228 }
229}
230
231bool CleanupPreviousUpdateAction::OnMergePercentageUpdate() {
232 double percentage = 0.0;
233 snapshot_->GetUpdateState(&percentage);
234 if (delegate_) {
235 // libsnapshot uses [0, 100] percentage but update_engine uses [0, 1].
236 delegate_->OnCleanupProgressUpdate(percentage / 100);
237 }
238
239 // Log if percentage increments by at least 1.
240 if (last_percentage_ < static_cast<unsigned int>(percentage)) {
241 last_percentage_ = percentage;
242 LOG(INFO) << "Waiting for merge to complete: " << last_percentage_ << "%.";
243 }
244
245 // Do not continue to wait for merge. Instead, let ProcessUpdateState
246 // return Merging directly so that we can ScheduleWaitForMerge() in
247 // MessageLoop.
248 return false;
249}
250
251bool CleanupPreviousUpdateAction::BeforeCancel() {
252 if (DeltaPerformer::ResetUpdateProgress(
253 prefs_,
254 false /* quick */,
255 false /* skip dynamic partitions metadata*/)) {
256 return true;
257 }
258
259 // ResetUpdateProgress might not work on stub prefs. Do additional checks.
260 LOG(WARNING) << "ProcessUpdateState returns Cancelled but cleanup failed.";
261
262 std::string val;
263 ignore_result(prefs_->GetString(kPrefsDynamicPartitionMetadataUpdated, &val));
264 if (val.empty()) {
265 LOG(INFO) << kPrefsDynamicPartitionMetadataUpdated
266 << " is empty, assuming successful cleanup";
267 return true;
268 }
269 LOG(WARNING)
270 << kPrefsDynamicPartitionMetadataUpdated << " is " << val
271 << ", not deleting snapshots even though UpdateState is Cancelled.";
272 cancel_failed_ = true;
273 return false;
274}
275
276void CleanupPreviousUpdateAction::InitiateMergeAndWait() {
277 TEST_AND_RETURN(running_);
278 LOG(INFO) << "Attempting to initiate merge.";
279
280 if (snapshot_->InitiateMerge()) {
281 WaitForMergeOrSchedule();
282 return;
283 }
284
285 LOG(WARNING) << "InitiateMerge failed.";
286 auto state = snapshot_->GetUpdateState();
Yifan Hongd976cc52020-02-25 14:51:42 -0800287 merge_stats_->set_state(state);
Yifan Hongdad0af82020-02-19 17:19:49 -0800288 if (state == UpdateState::Unverified) {
289 // We are stuck at unverified state. This can happen if the update has
290 // been applied, but it has not even been attempted yet (in libsnapshot,
291 // rollback indicator does not exist); for example, if update_engine
292 // restarts before the device reboots, then this state may be reached.
293 // Nothing should be done here.
294 LOG(WARNING) << "InitiateMerge leaves the device at "
295 << "UpdateState::Unverified. (Did update_engine "
296 << "restarted?)";
297 processor_->ActionComplete(this, ErrorCode::kSuccess);
298 return;
299 }
300
301 // State does seems to be advanced.
302 // It is possibly racy. For example, on a userdebug build, the user may
303 // manually initiate a merge with snapshotctl between last time
304 // update_engine checks UpdateState. Hence, just call
305 // WaitForMergeOrSchedule one more time.
306 LOG(WARNING) << "IniitateMerge failed but GetUpdateState returned "
307 << android::snapshot::UpdateState_Name(state)
308 << ", try to wait for merge again.";
309 WaitForMergeOrSchedule();
310 return;
311}
312
Yifan Hongd976cc52020-02-25 14:51:42 -0800313void CleanupPreviousUpdateAction::ReportMergeStats() {
314 auto result = merge_stats_->Finish();
315 if (result == nullptr) {
316 LOG(WARNING) << "Not reporting merge stats because "
317 "SnapshotMergeStats::Finish failed.";
318 return;
319 }
320
321#ifdef __ANDROID_RECOVERY__
322 LOG(INFO) << "Skip reporting merge stats in recovery.";
323#else
324 const auto& report = result->report();
325
326 if (report.state() == UpdateState::None ||
327 report.state() == UpdateState::Initiated ||
328 report.state() == UpdateState::Unverified) {
329 LOG(INFO) << "Not reporting merge stats because state is "
330 << android::snapshot::UpdateState_Name(report.state());
331 return;
332 }
333
334 auto passed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
335 result->merge_time());
336 LOG(INFO) << "Reporting merge stats: "
337 << android::snapshot::UpdateState_Name(report.state()) << " in "
338 << passed_ms.count() << "ms (resumed " << report.resume_count()
339 << " times)";
340#endif
341}
342
Yifan Hongdad0af82020-02-19 17:19:49 -0800343} // namespace chromeos_update_engine