Merge remote-tracking branch 'aosp/upstream-master' into merge
git merge aosp/upstream-master --commit -s recursive
Test: treehugger
Change-Id: Ifab9f47e1c5bea3898e78df0139d10842b41a44f
diff --git a/aosp/binder_service_android.cc b/aosp/binder_service_android.cc
new file mode 100644
index 0000000..ed76c4a
--- /dev/null
+++ b/aosp/binder_service_android.cc
@@ -0,0 +1,243 @@
+//
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/aosp/binder_service_android.h"
+
+#include <memory>
+
+#include <base/bind.h>
+#include <base/logging.h>
+#include <binderwrapper/binder_wrapper.h>
+#include <brillo/errors/error.h>
+#include <utils/String8.h>
+
+#include "update_engine/aosp/binder_service_android_common.h"
+
+using android::binder::Status;
+using android::os::IUpdateEngineCallback;
+using android::os::ParcelFileDescriptor;
+using std::string;
+using std::vector;
+using update_engine::UpdateEngineStatus;
+
+namespace chromeos_update_engine {
+
+BinderUpdateEngineAndroidService::BinderUpdateEngineAndroidService(
+ ServiceDelegateAndroidInterface* service_delegate)
+ : service_delegate_(service_delegate) {}
+
+void BinderUpdateEngineAndroidService::SendStatusUpdate(
+ const UpdateEngineStatus& update_engine_status) {
+ last_status_ = static_cast<int>(update_engine_status.status);
+ last_progress_ = update_engine_status.progress;
+ for (auto& callback : callbacks_) {
+ callback->onStatusUpdate(last_status_, last_progress_);
+ }
+}
+
+void BinderUpdateEngineAndroidService::SendPayloadApplicationComplete(
+ ErrorCode error_code) {
+ for (auto& callback : callbacks_) {
+ callback->onPayloadApplicationComplete(static_cast<int>(error_code));
+ }
+}
+
+Status BinderUpdateEngineAndroidService::bind(
+ const android::sp<IUpdateEngineCallback>& callback, bool* return_value) {
+ // Send an status update on connection (except when no update sent so far).
+ // Even though the status update is oneway, it still returns an erroneous
+ // status in case of a selinux denial. We should at least check this status
+ // and fails the binding.
+ if (last_status_ != -1) {
+ auto status = callback->onStatusUpdate(last_status_, last_progress_);
+ if (!status.isOk()) {
+ LOG(ERROR) << "Failed to call onStatusUpdate() from callback: "
+ << status.toString8();
+ *return_value = false;
+ return Status::ok();
+ }
+ }
+
+ callbacks_.emplace_back(callback);
+
+ const android::sp<IBinder>& callback_binder =
+ IUpdateEngineCallback::asBinder(callback);
+ auto binder_wrapper = android::BinderWrapper::Get();
+ binder_wrapper->RegisterForDeathNotifications(
+ callback_binder,
+ base::Bind(
+ base::IgnoreResult(&BinderUpdateEngineAndroidService::UnbindCallback),
+ base::Unretained(this),
+ base::Unretained(callback_binder.get())));
+
+ *return_value = true;
+ return Status::ok();
+}
+
+Status BinderUpdateEngineAndroidService::unbind(
+ const android::sp<IUpdateEngineCallback>& callback, bool* return_value) {
+ const android::sp<IBinder>& callback_binder =
+ IUpdateEngineCallback::asBinder(callback);
+ auto binder_wrapper = android::BinderWrapper::Get();
+ binder_wrapper->UnregisterForDeathNotifications(callback_binder);
+
+ *return_value = UnbindCallback(callback_binder.get());
+ return Status::ok();
+}
+
+Status BinderUpdateEngineAndroidService::applyPayload(
+ const android::String16& url,
+ int64_t payload_offset,
+ int64_t payload_size,
+ const vector<android::String16>& header_kv_pairs) {
+ const string payload_url{android::String8{url}.string()};
+ vector<string> str_headers = ToVecString(header_kv_pairs);
+
+ brillo::ErrorPtr error;
+ if (!service_delegate_->ApplyPayload(
+ payload_url, payload_offset, payload_size, str_headers, &error)) {
+ return ErrorPtrToStatus(error);
+ }
+ return Status::ok();
+}
+
+Status BinderUpdateEngineAndroidService::applyPayloadFd(
+ const ParcelFileDescriptor& pfd,
+ int64_t payload_offset,
+ int64_t payload_size,
+ const vector<android::String16>& header_kv_pairs) {
+ vector<string> str_headers = ToVecString(header_kv_pairs);
+
+ brillo::ErrorPtr error;
+ if (!service_delegate_->ApplyPayload(
+ pfd.get(), payload_offset, payload_size, str_headers, &error)) {
+ return ErrorPtrToStatus(error);
+ }
+ return Status::ok();
+}
+
+Status BinderUpdateEngineAndroidService::suspend() {
+ brillo::ErrorPtr error;
+ if (!service_delegate_->SuspendUpdate(&error))
+ return ErrorPtrToStatus(error);
+ return Status::ok();
+}
+
+Status BinderUpdateEngineAndroidService::resume() {
+ brillo::ErrorPtr error;
+ if (!service_delegate_->ResumeUpdate(&error))
+ return ErrorPtrToStatus(error);
+ return Status::ok();
+}
+
+Status BinderUpdateEngineAndroidService::cancel() {
+ brillo::ErrorPtr error;
+ if (!service_delegate_->CancelUpdate(&error))
+ return ErrorPtrToStatus(error);
+ return Status::ok();
+}
+
+Status BinderUpdateEngineAndroidService::resetStatus() {
+ brillo::ErrorPtr error;
+ if (!service_delegate_->ResetStatus(&error))
+ return ErrorPtrToStatus(error);
+ return Status::ok();
+}
+
+Status BinderUpdateEngineAndroidService::verifyPayloadApplicable(
+ const android::String16& metadata_filename, bool* return_value) {
+ const std::string payload_metadata{
+ android::String8{metadata_filename}.string()};
+ LOG(INFO) << "Received a request of verifying payload metadata in "
+ << payload_metadata << ".";
+ brillo::ErrorPtr error;
+ *return_value =
+ service_delegate_->VerifyPayloadApplicable(payload_metadata, &error);
+ if (error != nullptr)
+ return ErrorPtrToStatus(error);
+ return Status::ok();
+}
+
+bool BinderUpdateEngineAndroidService::UnbindCallback(const IBinder* callback) {
+ auto it = std::find_if(
+ callbacks_.begin(),
+ callbacks_.end(),
+ [&callback](const android::sp<IUpdateEngineCallback>& elem) {
+ return IUpdateEngineCallback::asBinder(elem).get() == callback;
+ });
+ if (it == callbacks_.end()) {
+ LOG(ERROR) << "Unable to unbind unknown callback.";
+ return false;
+ }
+ callbacks_.erase(it);
+ return true;
+}
+
+Status BinderUpdateEngineAndroidService::allocateSpaceForPayload(
+ const android::String16& metadata_filename,
+ const vector<android::String16>& header_kv_pairs,
+ int64_t* return_value) {
+ const std::string payload_metadata{
+ android::String8{metadata_filename}.string()};
+ vector<string> str_headers = ToVecString(header_kv_pairs);
+ LOG(INFO) << "Received a request of allocating space for " << payload_metadata
+ << ".";
+ brillo::ErrorPtr error;
+ *return_value =
+ static_cast<int64_t>(service_delegate_->AllocateSpaceForPayload(
+ payload_metadata, str_headers, &error));
+ if (error != nullptr)
+ return ErrorPtrToStatus(error);
+ return Status::ok();
+}
+
+class CleanupSuccessfulUpdateCallback
+ : public CleanupSuccessfulUpdateCallbackInterface {
+ public:
+ CleanupSuccessfulUpdateCallback(
+ const android::sp<IUpdateEngineCallback>& callback)
+ : callback_(callback) {}
+ void OnCleanupComplete(int32_t error_code) {
+ ignore_result(callback_->onPayloadApplicationComplete(error_code));
+ }
+ void OnCleanupProgressUpdate(double progress) {
+ ignore_result(callback_->onStatusUpdate(
+ static_cast<int32_t>(
+ update_engine::UpdateStatus::CLEANUP_PREVIOUS_UPDATE),
+ progress));
+ }
+ void RegisterForDeathNotifications(base::Closure unbind) {
+ const android::sp<android::IBinder>& callback_binder =
+ IUpdateEngineCallback::asBinder(callback_);
+ auto binder_wrapper = android::BinderWrapper::Get();
+ binder_wrapper->RegisterForDeathNotifications(callback_binder, unbind);
+ }
+
+ private:
+ android::sp<IUpdateEngineCallback> callback_;
+};
+
+Status BinderUpdateEngineAndroidService::cleanupSuccessfulUpdate(
+ const android::sp<IUpdateEngineCallback>& callback) {
+ brillo::ErrorPtr error;
+ service_delegate_->CleanupSuccessfulUpdate(
+ std::make_unique<CleanupSuccessfulUpdateCallback>(callback), &error);
+ if (error != nullptr)
+ return ErrorPtrToStatus(error);
+ return Status::ok();
+}
+
+} // namespace chromeos_update_engine
diff --git a/aosp/binder_service_android.h b/aosp/binder_service_android.h
new file mode 100644
index 0000000..f41fbdf
--- /dev/null
+++ b/aosp/binder_service_android.h
@@ -0,0 +1,99 @@
+//
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_AOSP_BINDER_SERVICE_ANDROID_H_
+#define UPDATE_ENGINE_AOSP_BINDER_SERVICE_ANDROID_H_
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include <utils/Errors.h>
+#include <utils/String16.h>
+#include <utils/StrongPointer.h>
+
+#include "android/os/BnUpdateEngine.h"
+#include "android/os/IUpdateEngineCallback.h"
+#include "update_engine/aosp/service_delegate_android_interface.h"
+#include "update_engine/common/service_observer_interface.h"
+
+namespace chromeos_update_engine {
+
+class BinderUpdateEngineAndroidService : public android::os::BnUpdateEngine,
+ public ServiceObserverInterface {
+ public:
+ explicit BinderUpdateEngineAndroidService(
+ ServiceDelegateAndroidInterface* service_delegate);
+ ~BinderUpdateEngineAndroidService() override = default;
+
+ const char* ServiceName() const { return "android.os.UpdateEngineService"; }
+
+ // ServiceObserverInterface overrides.
+ void SendStatusUpdate(
+ const update_engine::UpdateEngineStatus& update_engine_status) override;
+ void SendPayloadApplicationComplete(ErrorCode error_code) override;
+
+ // android::os::BnUpdateEngine overrides.
+ android::binder::Status applyPayload(
+ const android::String16& url,
+ int64_t payload_offset,
+ int64_t payload_size,
+ const std::vector<android::String16>& header_kv_pairs) override;
+ android::binder::Status applyPayloadFd(
+ const ::android::os::ParcelFileDescriptor& pfd,
+ int64_t payload_offset,
+ int64_t payload_size,
+ const std::vector<android::String16>& header_kv_pairs) override;
+ android::binder::Status bind(
+ const android::sp<android::os::IUpdateEngineCallback>& callback,
+ bool* return_value) override;
+ android::binder::Status unbind(
+ const android::sp<android::os::IUpdateEngineCallback>& callback,
+ bool* return_value) override;
+ android::binder::Status suspend() override;
+ android::binder::Status resume() override;
+ android::binder::Status cancel() override;
+ android::binder::Status resetStatus() override;
+ android::binder::Status verifyPayloadApplicable(
+ const android::String16& metadata_filename, bool* return_value) override;
+ android::binder::Status allocateSpaceForPayload(
+ const android::String16& metadata_filename,
+ const std::vector<android::String16>& header_kv_pairs,
+ int64_t* return_value) override;
+ android::binder::Status cleanupSuccessfulUpdate(
+ const android::sp<android::os::IUpdateEngineCallback>& callback) override;
+
+ private:
+ // Remove the passed |callback| from the list of registered callbacks. Called
+ // on unbind() or whenever the callback object is destroyed.
+ // Returns true on success.
+ bool UnbindCallback(const IBinder* callback);
+
+ // List of currently bound callbacks.
+ std::vector<android::sp<android::os::IUpdateEngineCallback>> callbacks_;
+
+ // Cached copy of the last status update sent. Used to send an initial
+ // notification when bind() is called from the client.
+ int last_status_{-1};
+ double last_progress_{0.0};
+
+ ServiceDelegateAndroidInterface* service_delegate_;
+};
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_AOSP_BINDER_SERVICE_ANDROID_H_
diff --git a/aosp/binder_service_android_common.h b/aosp/binder_service_android_common.h
new file mode 100644
index 0000000..223b32e
--- /dev/null
+++ b/aosp/binder_service_android_common.h
@@ -0,0 +1,45 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_AOSP_BINDER_SERVICE_ANDROID_COMMON_H_
+#define UPDATE_ENGINE_AOSP_BINDER_SERVICE_ANDROID_COMMON_H_
+
+#include <string>
+#include <vector>
+
+#include <binder/Status.h>
+
+namespace chromeos_update_engine {
+
+static inline android::binder::Status ErrorPtrToStatus(
+ const brillo::ErrorPtr& error) {
+ return android::binder::Status::fromServiceSpecificError(
+ 1, android::String8{error->GetMessage().c_str()});
+}
+
+static inline std::vector<std::string> ToVecString(
+ const std::vector<android::String16>& inp) {
+ std::vector<std::string> out;
+ out.reserve(inp.size());
+ for (const auto& e : inp) {
+ out.emplace_back(android::String8{e}.string());
+ }
+ return out;
+}
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_AOSP_BINDER_SERVICE_ANDROID_COMMON_H_
diff --git a/aosp/binder_service_stable_android.cc b/aosp/binder_service_stable_android.cc
new file mode 100644
index 0000000..17b35ee
--- /dev/null
+++ b/aosp/binder_service_stable_android.cc
@@ -0,0 +1,132 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/aosp/binder_service_stable_android.h"
+
+#include <memory>
+
+#include <base/bind.h>
+#include <base/logging.h>
+#include <binderwrapper/binder_wrapper.h>
+#include <brillo/errors/error.h>
+#include <utils/String8.h>
+
+#include "update_engine/aosp/binder_service_android_common.h"
+
+using android::binder::Status;
+using android::os::IUpdateEngineStableCallback;
+using android::os::ParcelFileDescriptor;
+using std::string;
+using std::vector;
+using update_engine::UpdateEngineStatus;
+
+namespace chromeos_update_engine {
+
+BinderUpdateEngineAndroidStableService::BinderUpdateEngineAndroidStableService(
+ ServiceDelegateAndroidInterface* service_delegate)
+ : service_delegate_(service_delegate) {}
+
+void BinderUpdateEngineAndroidStableService::SendStatusUpdate(
+ const UpdateEngineStatus& update_engine_status) {
+ last_status_ = static_cast<int>(update_engine_status.status);
+ last_progress_ = update_engine_status.progress;
+ if (callback_) {
+ callback_->onStatusUpdate(last_status_, last_progress_);
+ }
+}
+
+void BinderUpdateEngineAndroidStableService::SendPayloadApplicationComplete(
+ ErrorCode error_code) {
+ if (callback_) {
+ callback_->onPayloadApplicationComplete(static_cast<int>(error_code));
+ }
+}
+
+Status BinderUpdateEngineAndroidStableService::bind(
+ const android::sp<IUpdateEngineStableCallback>& callback,
+ bool* return_value) {
+ // Reject binding if another callback is already bound.
+ if (callback_ != nullptr) {
+ LOG(ERROR) << "Another callback is already bound. Can't bind new callback.";
+ *return_value = false;
+ return Status::ok();
+ }
+
+ // See BinderUpdateEngineAndroidService::bind.
+ if (last_status_ != -1) {
+ auto status = callback->onStatusUpdate(last_status_, last_progress_);
+ if (!status.isOk()) {
+ LOG(ERROR) << "Failed to call onStatusUpdate() from callback: "
+ << status.toString8();
+ *return_value = false;
+ return Status::ok();
+ }
+ }
+
+ callback_ = callback;
+
+ const android::sp<IBinder>& callback_binder =
+ IUpdateEngineStableCallback::asBinder(callback);
+ auto binder_wrapper = android::BinderWrapper::Get();
+ binder_wrapper->RegisterForDeathNotifications(
+ callback_binder,
+ base::Bind(base::IgnoreResult(
+ &BinderUpdateEngineAndroidStableService::UnbindCallback),
+ base::Unretained(this),
+ base::Unretained(callback_binder.get())));
+
+ *return_value = true;
+ return Status::ok();
+}
+
+Status BinderUpdateEngineAndroidStableService::unbind(
+ const android::sp<IUpdateEngineStableCallback>& callback,
+ bool* return_value) {
+ const android::sp<IBinder>& callback_binder =
+ IUpdateEngineStableCallback::asBinder(callback);
+ auto binder_wrapper = android::BinderWrapper::Get();
+ binder_wrapper->UnregisterForDeathNotifications(callback_binder);
+
+ *return_value = UnbindCallback(callback_binder.get());
+ return Status::ok();
+}
+
+Status BinderUpdateEngineAndroidStableService::applyPayloadFd(
+ const ParcelFileDescriptor& pfd,
+ int64_t payload_offset,
+ int64_t payload_size,
+ const vector<android::String16>& header_kv_pairs) {
+ vector<string> str_headers = ToVecString(header_kv_pairs);
+
+ brillo::ErrorPtr error;
+ if (!service_delegate_->ApplyPayload(
+ pfd.get(), payload_offset, payload_size, str_headers, &error)) {
+ return ErrorPtrToStatus(error);
+ }
+ return Status::ok();
+}
+
+bool BinderUpdateEngineAndroidStableService::UnbindCallback(
+ const IBinder* callback) {
+ if (IUpdateEngineStableCallback::asBinder(callback_).get() != callback) {
+ LOG(ERROR) << "Unable to unbind unknown callback.";
+ return false;
+ }
+ callback_ = nullptr;
+ return true;
+}
+
+} // namespace chromeos_update_engine
diff --git a/aosp/binder_service_stable_android.h b/aosp/binder_service_stable_android.h
new file mode 100644
index 0000000..212afaa
--- /dev/null
+++ b/aosp/binder_service_stable_android.h
@@ -0,0 +1,85 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_AOSP_BINDER_SERVICE_STABLE_ANDROID_H_
+#define UPDATE_ENGINE_AOSP_BINDER_SERVICE_STABLE_ANDROID_H_
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include <utils/Errors.h>
+#include <utils/String16.h>
+#include <utils/StrongPointer.h>
+
+#include "android/os/BnUpdateEngineStable.h"
+#include "android/os/IUpdateEngineStableCallback.h"
+#include "update_engine/aosp/service_delegate_android_interface.h"
+#include "update_engine/common/service_observer_interface.h"
+
+namespace chromeos_update_engine {
+
+class BinderUpdateEngineAndroidStableService
+ : public android::os::BnUpdateEngineStable,
+ public ServiceObserverInterface {
+ public:
+ explicit BinderUpdateEngineAndroidStableService(
+ ServiceDelegateAndroidInterface* service_delegate);
+ ~BinderUpdateEngineAndroidStableService() override = default;
+
+ const char* ServiceName() const {
+ return "android.os.UpdateEngineStableService";
+ }
+
+ // ServiceObserverInterface overrides.
+ void SendStatusUpdate(
+ const update_engine::UpdateEngineStatus& update_engine_status) override;
+ void SendPayloadApplicationComplete(ErrorCode error_code) override;
+
+ // android::os::BnUpdateEngineStable overrides.
+ android::binder::Status applyPayloadFd(
+ const ::android::os::ParcelFileDescriptor& pfd,
+ int64_t payload_offset,
+ int64_t payload_size,
+ const std::vector<android::String16>& header_kv_pairs) override;
+ android::binder::Status bind(
+ const android::sp<android::os::IUpdateEngineStableCallback>& callback,
+ bool* return_value) override;
+ android::binder::Status unbind(
+ const android::sp<android::os::IUpdateEngineStableCallback>& callback,
+ bool* return_value) override;
+
+ private:
+ // Remove the passed |callback| from the list of registered callbacks. Called
+ // on unbind() or whenever the callback object is destroyed.
+ // Returns true on success.
+ bool UnbindCallback(const IBinder* callback);
+
+ // Bound callback. The stable interface only supports one callback at a time.
+ android::sp<android::os::IUpdateEngineStableCallback> callback_;
+
+ // Cached copy of the last status update sent. Used to send an initial
+ // notification when bind() is called from the client.
+ int last_status_{-1};
+ double last_progress_{0.0};
+
+ ServiceDelegateAndroidInterface* service_delegate_;
+};
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_AOSP_BINDER_SERVICE_STABLE_ANDROID_H_
diff --git a/aosp/boot_control_android.cc b/aosp/boot_control_android.cc
new file mode 100644
index 0000000..bda65be
--- /dev/null
+++ b/aosp/boot_control_android.cc
@@ -0,0 +1,186 @@
+//
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/aosp/boot_control_android.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include <base/bind.h>
+#include <base/logging.h>
+#include <bootloader_message/bootloader_message.h>
+#include <brillo/message_loops/message_loop.h>
+
+#include "update_engine/aosp/dynamic_partition_control_android.h"
+#include "update_engine/common/utils.h"
+
+using std::string;
+
+using android::dm::DmDeviceState;
+using android::hardware::hidl_string;
+using android::hardware::Return;
+using android::hardware::boot::V1_0::BoolResult;
+using android::hardware::boot::V1_0::CommandResult;
+using android::hardware::boot::V1_0::IBootControl;
+using Slot = chromeos_update_engine::BootControlInterface::Slot;
+
+namespace {
+
+auto StoreResultCallback(CommandResult* dest) {
+ return [dest](const CommandResult& result) { *dest = result; };
+}
+} // namespace
+
+namespace chromeos_update_engine {
+
+namespace boot_control {
+
+// Factory defined in boot_control.h.
+std::unique_ptr<BootControlInterface> CreateBootControl() {
+ auto boot_control = std::make_unique<BootControlAndroid>();
+ if (!boot_control->Init()) {
+ return nullptr;
+ }
+ return std::move(boot_control);
+}
+
+} // namespace boot_control
+
+bool BootControlAndroid::Init() {
+ module_ = IBootControl::getService();
+ if (module_ == nullptr) {
+ LOG(ERROR) << "Error getting bootctrl HIDL module.";
+ return false;
+ }
+
+ LOG(INFO) << "Loaded boot control hidl hal.";
+
+ dynamic_control_ = std::make_unique<DynamicPartitionControlAndroid>();
+
+ return true;
+}
+
+unsigned int BootControlAndroid::GetNumSlots() const {
+ return module_->getNumberSlots();
+}
+
+BootControlInterface::Slot BootControlAndroid::GetCurrentSlot() const {
+ return module_->getCurrentSlot();
+}
+
+bool BootControlAndroid::GetPartitionDevice(const std::string& partition_name,
+ BootControlInterface::Slot slot,
+ bool not_in_payload,
+ std::string* device,
+ bool* is_dynamic) const {
+ return dynamic_control_->GetPartitionDevice(partition_name,
+ slot,
+ GetCurrentSlot(),
+ not_in_payload,
+ device,
+ is_dynamic);
+}
+
+bool BootControlAndroid::GetPartitionDevice(const string& partition_name,
+ BootControlInterface::Slot slot,
+ string* device) const {
+ return GetPartitionDevice(
+ partition_name, slot, false /* not_in_payload */, device, nullptr);
+}
+
+bool BootControlAndroid::IsSlotBootable(Slot slot) const {
+ Return<BoolResult> ret = module_->isSlotBootable(slot);
+ if (!ret.isOk()) {
+ LOG(ERROR) << "Unable to determine if slot " << SlotName(slot)
+ << " is bootable: " << ret.description();
+ return false;
+ }
+ if (ret == BoolResult::INVALID_SLOT) {
+ LOG(ERROR) << "Invalid slot: " << SlotName(slot);
+ return false;
+ }
+ return ret == BoolResult::TRUE;
+}
+
+bool BootControlAndroid::MarkSlotUnbootable(Slot slot) {
+ CommandResult result;
+ auto ret = module_->setSlotAsUnbootable(slot, StoreResultCallback(&result));
+ if (!ret.isOk()) {
+ LOG(ERROR) << "Unable to call MarkSlotUnbootable for slot "
+ << SlotName(slot) << ": " << ret.description();
+ return false;
+ }
+ if (!result.success) {
+ LOG(ERROR) << "Unable to mark slot " << SlotName(slot)
+ << " as unbootable: " << result.errMsg.c_str();
+ }
+ return result.success;
+}
+
+bool BootControlAndroid::SetActiveBootSlot(Slot slot) {
+ CommandResult result;
+ auto ret = module_->setActiveBootSlot(slot, StoreResultCallback(&result));
+ if (!ret.isOk()) {
+ LOG(ERROR) << "Unable to call SetActiveBootSlot for slot " << SlotName(slot)
+ << ": " << ret.description();
+ return false;
+ }
+ if (!result.success) {
+ LOG(ERROR) << "Unable to set the active slot to slot " << SlotName(slot)
+ << ": " << result.errMsg.c_str();
+ }
+ return result.success;
+}
+
+bool BootControlAndroid::MarkBootSuccessfulAsync(
+ base::Callback<void(bool)> callback) {
+ CommandResult result;
+ auto ret = module_->markBootSuccessful(StoreResultCallback(&result));
+ if (!ret.isOk()) {
+ LOG(ERROR) << "Unable to call MarkBootSuccessful: " << ret.description();
+ return false;
+ }
+ if (!result.success) {
+ LOG(ERROR) << "Unable to mark boot successful: " << result.errMsg.c_str();
+ }
+ return brillo::MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(callback, result.success)) !=
+ brillo::MessageLoop::kTaskIdNull;
+}
+
+bool BootControlAndroid::IsSlotMarkedSuccessful(
+ BootControlInterface::Slot slot) const {
+ Return<BoolResult> ret = module_->isSlotMarkedSuccessful(slot);
+ CommandResult result;
+ if (!ret.isOk()) {
+ LOG(ERROR) << "Unable to determine if slot " << SlotName(slot)
+ << " is marked successful: " << ret.description();
+ return false;
+ }
+ if (ret == BoolResult::INVALID_SLOT) {
+ LOG(ERROR) << "Invalid slot: " << SlotName(slot);
+ return false;
+ }
+ return ret == BoolResult::TRUE;
+}
+
+DynamicPartitionControlInterface*
+BootControlAndroid::GetDynamicPartitionControl() {
+ return dynamic_control_.get();
+}
+
+} // namespace chromeos_update_engine
diff --git a/aosp/boot_control_android.h b/aosp/boot_control_android.h
new file mode 100644
index 0000000..e288723
--- /dev/null
+++ b/aosp/boot_control_android.h
@@ -0,0 +1,73 @@
+//
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_AOSP_BOOT_CONTROL_ANDROID_H_
+#define UPDATE_ENGINE_AOSP_BOOT_CONTROL_ANDROID_H_
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include <android/hardware/boot/1.0/IBootControl.h>
+#include <liblp/builder.h>
+
+#include "update_engine/aosp/dynamic_partition_control_android.h"
+#include "update_engine/common/boot_control.h"
+#include "update_engine/common/dynamic_partition_control_interface.h"
+
+namespace chromeos_update_engine {
+
+// The Android implementation of the BootControlInterface. This implementation
+// uses the libhardware's boot_control HAL to access the bootloader.
+class BootControlAndroid : public BootControlInterface {
+ public:
+ BootControlAndroid() = default;
+ ~BootControlAndroid() = default;
+
+ // Load boot_control HAL implementation using libhardware and
+ // initializes it. Returns false if an error occurred.
+ bool Init();
+
+ // BootControlInterface overrides.
+ unsigned int GetNumSlots() const override;
+ BootControlInterface::Slot GetCurrentSlot() const override;
+ bool GetPartitionDevice(const std::string& partition_name,
+ BootControlInterface::Slot slot,
+ bool not_in_payload,
+ std::string* device,
+ bool* is_dynamic) const override;
+ bool GetPartitionDevice(const std::string& partition_name,
+ BootControlInterface::Slot slot,
+ std::string* device) const override;
+ bool IsSlotBootable(BootControlInterface::Slot slot) const override;
+ bool MarkSlotUnbootable(BootControlInterface::Slot slot) override;
+ bool SetActiveBootSlot(BootControlInterface::Slot slot) override;
+ bool MarkBootSuccessfulAsync(base::Callback<void(bool)> callback) override;
+ bool IsSlotMarkedSuccessful(BootControlInterface::Slot slot) const override;
+ DynamicPartitionControlInterface* GetDynamicPartitionControl() override;
+
+ private:
+ ::android::sp<::android::hardware::boot::V1_0::IBootControl> module_;
+ std::unique_ptr<DynamicPartitionControlAndroid> dynamic_control_;
+
+ friend class BootControlAndroidTest;
+
+ DISALLOW_COPY_AND_ASSIGN(BootControlAndroid);
+};
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_AOSP_BOOT_CONTROL_ANDROID_H_
diff --git a/aosp/cleanup_previous_update_action.cc b/aosp/cleanup_previous_update_action.cc
new file mode 100644
index 0000000..16cb9fe
--- /dev/null
+++ b/aosp/cleanup_previous_update_action.cc
@@ -0,0 +1,474 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#include "update_engine/aosp/cleanup_previous_update_action.h"
+
+#include <chrono> // NOLINT(build/c++11) -- for merge times
+#include <functional>
+#include <string>
+#include <type_traits>
+
+#include <android-base/properties.h>
+#include <base/bind.h>
+
+#ifndef __ANDROID_RECOVERY__
+#include <statslog.h>
+#endif
+
+#include "update_engine/common/utils.h"
+#include "update_engine/payload_consumer/delta_performer.h"
+
+using android::base::GetBoolProperty;
+using android::snapshot::ISnapshotManager;
+using android::snapshot::SnapshotMergeStats;
+using android::snapshot::UpdateState;
+using brillo::MessageLoop;
+
+constexpr char kBootCompletedProp[] = "sys.boot_completed";
+// Interval to check sys.boot_completed.
+constexpr auto kCheckBootCompletedInterval = base::TimeDelta::FromSeconds(2);
+// Interval to check IBootControl::isSlotMarkedSuccessful
+constexpr auto kCheckSlotMarkedSuccessfulInterval =
+ base::TimeDelta::FromSeconds(2);
+// Interval to call SnapshotManager::ProcessUpdateState
+constexpr auto kWaitForMergeInterval = base::TimeDelta::FromSeconds(2);
+
+#ifdef __ANDROID_RECOVERY__
+static constexpr bool kIsRecovery = true;
+#else
+static constexpr bool kIsRecovery = false;
+#endif
+
+namespace chromeos_update_engine {
+
+CleanupPreviousUpdateAction::CleanupPreviousUpdateAction(
+ PrefsInterface* prefs,
+ BootControlInterface* boot_control,
+ android::snapshot::ISnapshotManager* snapshot,
+ CleanupPreviousUpdateActionDelegateInterface* delegate)
+ : prefs_(prefs),
+ boot_control_(boot_control),
+ snapshot_(snapshot),
+ delegate_(delegate),
+ running_(false),
+ cancel_failed_(false),
+ last_percentage_(0),
+ merge_stats_(nullptr) {}
+
+CleanupPreviousUpdateAction::~CleanupPreviousUpdateAction() {
+ StopActionInternal();
+}
+
+void CleanupPreviousUpdateAction::PerformAction() {
+ StartActionInternal();
+}
+
+void CleanupPreviousUpdateAction::TerminateProcessing() {
+ StopActionInternal();
+}
+
+void CleanupPreviousUpdateAction::ResumeAction() {
+ StartActionInternal();
+}
+
+void CleanupPreviousUpdateAction::SuspendAction() {
+ StopActionInternal();
+}
+
+void CleanupPreviousUpdateAction::ActionCompleted(ErrorCode error_code) {
+ StopActionInternal();
+ ReportMergeStats();
+ metadata_device_ = nullptr;
+}
+
+std::string CleanupPreviousUpdateAction::Type() const {
+ return StaticType();
+}
+
+std::string CleanupPreviousUpdateAction::StaticType() {
+ return "CleanupPreviousUpdateAction";
+}
+
+// This function is called at the beginning of all delayed functions. By
+// resetting |scheduled_task_|, the delayed function acknowledges that the task
+// has already been executed, therefore there's no need to cancel it in the
+// future. This avoids StopActionInternal() from resetting task IDs in an
+// unexpected way because task IDs could be reused.
+void CleanupPreviousUpdateAction::AcknowledgeTaskExecuted() {
+ if (scheduled_task_ != MessageLoop::kTaskIdNull) {
+ LOG(INFO) << "Executing task " << scheduled_task_;
+ }
+ scheduled_task_ = MessageLoop::kTaskIdNull;
+}
+
+// Check that scheduled_task_ is a valid task ID. Otherwise, terminate the
+// action.
+void CleanupPreviousUpdateAction::CheckTaskScheduled(std::string_view name) {
+ if (scheduled_task_ == MessageLoop::kTaskIdNull) {
+ LOG(ERROR) << "Unable to schedule " << name;
+ processor_->ActionComplete(this, ErrorCode::kError);
+ } else {
+ LOG(INFO) << "CleanupPreviousUpdateAction scheduled task ID "
+ << scheduled_task_ << " for " << name;
+ }
+}
+
+void CleanupPreviousUpdateAction::StopActionInternal() {
+ LOG(INFO) << "Stopping/suspending/completing CleanupPreviousUpdateAction";
+ running_ = false;
+
+ if (scheduled_task_ != MessageLoop::kTaskIdNull) {
+ if (MessageLoop::current()->CancelTask(scheduled_task_)) {
+ LOG(INFO) << "CleanupPreviousUpdateAction cancelled pending task ID "
+ << scheduled_task_;
+ } else {
+ LOG(ERROR) << "CleanupPreviousUpdateAction unable to cancel task ID "
+ << scheduled_task_;
+ }
+ }
+ scheduled_task_ = MessageLoop::kTaskIdNull;
+}
+
+void CleanupPreviousUpdateAction::StartActionInternal() {
+ CHECK(prefs_);
+ CHECK(boot_control_);
+
+ LOG(INFO) << "Starting/resuming CleanupPreviousUpdateAction";
+ running_ = true;
+ // Do nothing on non-VAB device.
+ if (!boot_control_->GetDynamicPartitionControl()
+ ->GetVirtualAbFeatureFlag()
+ .IsEnabled()) {
+ processor_->ActionComplete(this, ErrorCode::kSuccess);
+ return;
+ }
+ // SnapshotManager must be available on VAB devices.
+ CHECK(snapshot_ != nullptr);
+ merge_stats_ = snapshot_->GetSnapshotMergeStatsInstance();
+ CHECK(merge_stats_ != nullptr);
+ WaitBootCompletedOrSchedule();
+}
+
+void CleanupPreviousUpdateAction::ScheduleWaitBootCompleted() {
+ TEST_AND_RETURN(running_);
+ scheduled_task_ = MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&CleanupPreviousUpdateAction::WaitBootCompletedOrSchedule,
+ base::Unretained(this)),
+ kCheckBootCompletedInterval);
+ CheckTaskScheduled("WaitBootCompleted");
+}
+
+void CleanupPreviousUpdateAction::WaitBootCompletedOrSchedule() {
+ AcknowledgeTaskExecuted();
+ TEST_AND_RETURN(running_);
+ if (!kIsRecovery &&
+ !android::base::GetBoolProperty(kBootCompletedProp, false)) {
+ // repeat
+ ScheduleWaitBootCompleted();
+ return;
+ }
+
+ LOG(INFO) << "Boot completed, waiting on markBootSuccessful()";
+ CheckSlotMarkedSuccessfulOrSchedule();
+}
+
+void CleanupPreviousUpdateAction::ScheduleWaitMarkBootSuccessful() {
+ TEST_AND_RETURN(running_);
+ scheduled_task_ = MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(
+ &CleanupPreviousUpdateAction::CheckSlotMarkedSuccessfulOrSchedule,
+ base::Unretained(this)),
+ kCheckSlotMarkedSuccessfulInterval);
+ CheckTaskScheduled("WaitMarkBootSuccessful");
+}
+
+void CleanupPreviousUpdateAction::CheckSlotMarkedSuccessfulOrSchedule() {
+ AcknowledgeTaskExecuted();
+ TEST_AND_RETURN(running_);
+ if (!kIsRecovery &&
+ !boot_control_->IsSlotMarkedSuccessful(boot_control_->GetCurrentSlot())) {
+ ScheduleWaitMarkBootSuccessful();
+ }
+
+ if (metadata_device_ == nullptr) {
+ metadata_device_ = snapshot_->EnsureMetadataMounted();
+ }
+
+ if (metadata_device_ == nullptr) {
+ LOG(ERROR) << "Failed to mount /metadata.";
+ // If metadata is erased but not formatted, it is possible to not mount
+ // it in recovery. It is safe to skip CleanupPreviousUpdateAction.
+ processor_->ActionComplete(
+ this, kIsRecovery ? ErrorCode::kSuccess : ErrorCode::kError);
+ return;
+ }
+
+ if (kIsRecovery) {
+ auto snapshots_created =
+ snapshot_->RecoveryCreateSnapshotDevices(metadata_device_);
+ switch (snapshots_created) {
+ case android::snapshot::CreateResult::CREATED: {
+ // If previous update has not finished merging, snapshots exists and are
+ // created here so that ProcessUpdateState can proceed.
+ LOG(INFO) << "Snapshot devices are created";
+ break;
+ }
+ case android::snapshot::CreateResult::NOT_CREATED: {
+ // If there is no previous update, no snapshot devices are created and
+ // ProcessUpdateState will return immediately. Hence, NOT_CREATED is not
+ // considered an error.
+ LOG(INFO) << "Snapshot devices are not created";
+ break;
+ }
+ case android::snapshot::CreateResult::ERROR:
+ default: {
+ LOG(ERROR)
+ << "Failed to create snapshot devices (CreateResult = "
+ << static_cast<
+ std::underlying_type_t<android::snapshot::CreateResult>>(
+ snapshots_created);
+ processor_->ActionComplete(this, ErrorCode::kError);
+ return;
+ }
+ }
+ }
+
+ if (!merge_stats_->Start()) {
+ // Not an error because CleanupPreviousUpdateAction may be paused and
+ // resumed while kernel continues merging snapshots in the background.
+ LOG(WARNING) << "SnapshotMergeStats::Start failed.";
+ }
+ LOG(INFO) << "Waiting for any previous merge request to complete. "
+ << "This can take up to several minutes.";
+ WaitForMergeOrSchedule();
+}
+
+void CleanupPreviousUpdateAction::ScheduleWaitForMerge() {
+ TEST_AND_RETURN(running_);
+ scheduled_task_ = MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&CleanupPreviousUpdateAction::WaitForMergeOrSchedule,
+ base::Unretained(this)),
+ kWaitForMergeInterval);
+ CheckTaskScheduled("WaitForMerge");
+}
+
+void CleanupPreviousUpdateAction::WaitForMergeOrSchedule() {
+ AcknowledgeTaskExecuted();
+ TEST_AND_RETURN(running_);
+ auto state = snapshot_->ProcessUpdateState(
+ std::bind(&CleanupPreviousUpdateAction::OnMergePercentageUpdate, this),
+ std::bind(&CleanupPreviousUpdateAction::BeforeCancel, this));
+ merge_stats_->set_state(state);
+
+ switch (state) {
+ case UpdateState::None: {
+ LOG(INFO) << "Can't find any snapshot to merge.";
+ ErrorCode error_code = ErrorCode::kSuccess;
+ if (!snapshot_->CancelUpdate()) {
+ error_code = ErrorCode::kError;
+ LOG(INFO) << "Failed to call SnapshotManager::CancelUpdate().";
+ }
+ processor_->ActionComplete(this, error_code);
+ return;
+ }
+
+ case UpdateState::Initiated: {
+ LOG(ERROR) << "Previous update has not been completed, not cleaning up";
+ processor_->ActionComplete(this, ErrorCode::kSuccess);
+ return;
+ }
+
+ case UpdateState::Unverified: {
+ InitiateMergeAndWait();
+ return;
+ }
+
+ case UpdateState::Merging: {
+ ScheduleWaitForMerge();
+ return;
+ }
+
+ case UpdateState::MergeNeedsReboot: {
+ LOG(ERROR) << "Need reboot to finish merging.";
+ processor_->ActionComplete(this, ErrorCode::kError);
+ return;
+ }
+
+ case UpdateState::MergeCompleted: {
+ LOG(INFO) << "Merge finished with state MergeCompleted.";
+ processor_->ActionComplete(this, ErrorCode::kSuccess);
+ return;
+ }
+
+ case UpdateState::MergeFailed: {
+ LOG(ERROR) << "Merge failed. Device may be corrupted.";
+ processor_->ActionComplete(this, ErrorCode::kDeviceCorrupted);
+ return;
+ }
+
+ case UpdateState::Cancelled: {
+ // DeltaPerformer::ResetUpdateProgress failed, hence snapshots are
+ // not deleted to avoid inconsistency.
+ // Nothing can be done here; just try next time.
+ ErrorCode error_code =
+ cancel_failed_ ? ErrorCode::kError : ErrorCode::kSuccess;
+ processor_->ActionComplete(this, error_code);
+ return;
+ }
+
+ default: {
+ // Protobuf has some reserved enum values, so a default case is needed.
+ LOG(FATAL) << "SnapshotManager::ProcessUpdateState returns "
+ << static_cast<int32_t>(state);
+ }
+ }
+}
+
+bool CleanupPreviousUpdateAction::OnMergePercentageUpdate() {
+ double percentage = 0.0;
+ snapshot_->GetUpdateState(&percentage);
+ if (delegate_) {
+ // libsnapshot uses [0, 100] percentage but update_engine uses [0, 1].
+ delegate_->OnCleanupProgressUpdate(percentage / 100);
+ }
+
+ // Log if percentage increments by at least 1.
+ if (last_percentage_ < static_cast<unsigned int>(percentage)) {
+ last_percentage_ = percentage;
+ LOG(INFO) << "Waiting for merge to complete: " << last_percentage_ << "%.";
+ }
+
+ // Do not continue to wait for merge. Instead, let ProcessUpdateState
+ // return Merging directly so that we can ScheduleWaitForMerge() in
+ // MessageLoop.
+ return false;
+}
+
+bool CleanupPreviousUpdateAction::BeforeCancel() {
+ if (DeltaPerformer::ResetUpdateProgress(
+ prefs_,
+ false /* quick */,
+ false /* skip dynamic partitions metadata*/)) {
+ return true;
+ }
+
+ // ResetUpdateProgress might not work on stub prefs. Do additional checks.
+ LOG(WARNING) << "ProcessUpdateState returns Cancelled but cleanup failed.";
+
+ std::string val;
+ ignore_result(prefs_->GetString(kPrefsDynamicPartitionMetadataUpdated, &val));
+ if (val.empty()) {
+ LOG(INFO) << kPrefsDynamicPartitionMetadataUpdated
+ << " is empty, assuming successful cleanup";
+ return true;
+ }
+ LOG(WARNING)
+ << kPrefsDynamicPartitionMetadataUpdated << " is " << val
+ << ", not deleting snapshots even though UpdateState is Cancelled.";
+ cancel_failed_ = true;
+ return false;
+}
+
+void CleanupPreviousUpdateAction::InitiateMergeAndWait() {
+ TEST_AND_RETURN(running_);
+ LOG(INFO) << "Attempting to initiate merge.";
+ // suspend the VAB merge when running a DSU
+ if (GetBoolProperty("ro.gsid.image_running", false)) {
+ LOG(WARNING) << "Suspend the VAB merge when running a DSU.";
+ processor_->ActionComplete(this, ErrorCode::kError);
+ return;
+ }
+
+ uint64_t cow_file_size;
+ if (snapshot_->InitiateMerge(&cow_file_size)) {
+ merge_stats_->set_cow_file_size(cow_file_size);
+ WaitForMergeOrSchedule();
+ return;
+ }
+
+ LOG(WARNING) << "InitiateMerge failed.";
+ auto state = snapshot_->GetUpdateState();
+ merge_stats_->set_state(state);
+ if (state == UpdateState::Unverified) {
+ // We are stuck at unverified state. This can happen if the update has
+ // been applied, but it has not even been attempted yet (in libsnapshot,
+ // rollback indicator does not exist); for example, if update_engine
+ // restarts before the device reboots, then this state may be reached.
+ // Nothing should be done here.
+ LOG(WARNING) << "InitiateMerge leaves the device at "
+ << "UpdateState::Unverified. (Did update_engine "
+ << "restarted?)";
+ processor_->ActionComplete(this, ErrorCode::kSuccess);
+ return;
+ }
+
+ // State does seems to be advanced.
+ // It is possibly racy. For example, on a userdebug build, the user may
+ // manually initiate a merge with snapshotctl between last time
+ // update_engine checks UpdateState. Hence, just call
+ // WaitForMergeOrSchedule one more time.
+ LOG(WARNING) << "IniitateMerge failed but GetUpdateState returned "
+ << android::snapshot::UpdateState_Name(state)
+ << ", try to wait for merge again.";
+ WaitForMergeOrSchedule();
+ return;
+}
+
+void CleanupPreviousUpdateAction::ReportMergeStats() {
+ auto result = merge_stats_->Finish();
+ if (result == nullptr) {
+ LOG(WARNING) << "Not reporting merge stats because "
+ "SnapshotMergeStats::Finish failed.";
+ return;
+ }
+
+#ifdef __ANDROID_RECOVERY__
+ LOG(INFO) << "Skip reporting merge stats in recovery.";
+#else
+ const auto& report = result->report();
+
+ if (report.state() == UpdateState::None ||
+ report.state() == UpdateState::Initiated ||
+ report.state() == UpdateState::Unverified) {
+ LOG(INFO) << "Not reporting merge stats because state is "
+ << android::snapshot::UpdateState_Name(report.state());
+ return;
+ }
+
+ auto passed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
+ result->merge_time());
+
+ bool vab_retrofit = boot_control_->GetDynamicPartitionControl()
+ ->GetVirtualAbFeatureFlag()
+ .IsRetrofit();
+
+ LOG(INFO) << "Reporting merge stats: "
+ << android::snapshot::UpdateState_Name(report.state()) << " in "
+ << passed_ms.count() << "ms (resumed " << report.resume_count()
+ << " times), using " << report.cow_file_size()
+ << " bytes of COW image.";
+ android::util::stats_write(android::util::SNAPSHOT_MERGE_REPORTED,
+ static_cast<int32_t>(report.state()),
+ static_cast<int64_t>(passed_ms.count()),
+ static_cast<int32_t>(report.resume_count()),
+ vab_retrofit,
+ static_cast<int64_t>(report.cow_file_size()));
+#endif
+}
+
+} // namespace chromeos_update_engine
diff --git a/aosp/cleanup_previous_update_action.h b/aosp/cleanup_previous_update_action.h
new file mode 100644
index 0000000..b93c557
--- /dev/null
+++ b/aosp/cleanup_previous_update_action.h
@@ -0,0 +1,103 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_AOSP_CLEANUP_PREVIOUS_UPDATE_ACTION_H_
+#define UPDATE_ENGINE_AOSP_CLEANUP_PREVIOUS_UPDATE_ACTION_H_
+
+#include <chrono> // NOLINT(build/c++11) -- for merge times
+#include <memory>
+#include <string>
+#include <string_view>
+
+#include <brillo/message_loops/message_loop.h>
+#include <libsnapshot/snapshot.h>
+#include <libsnapshot/snapshot_stats.h>
+
+#include "update_engine/common/action.h"
+#include "update_engine/common/boot_control_interface.h"
+#include "update_engine/common/cleanup_previous_update_action_delegate.h"
+#include "update_engine/common/error_code.h"
+#include "update_engine/common/prefs_interface.h"
+
+namespace chromeos_update_engine {
+
+class CleanupPreviousUpdateAction;
+
+template <>
+class ActionTraits<CleanupPreviousUpdateAction> {
+ public:
+ typedef NoneType InputObjectType;
+ typedef NoneType OutputObjectType;
+};
+
+// On Android Virtual A/B devices, clean up snapshots from previous update
+// attempt. See DynamicPartitionControlAndroid::CleanupSuccessfulUpdate.
+class CleanupPreviousUpdateAction : public Action<CleanupPreviousUpdateAction> {
+ public:
+ CleanupPreviousUpdateAction(
+ PrefsInterface* prefs,
+ BootControlInterface* boot_control,
+ android::snapshot::ISnapshotManager* snapshot,
+ CleanupPreviousUpdateActionDelegateInterface* delegate);
+ ~CleanupPreviousUpdateAction();
+
+ void PerformAction() override;
+ void SuspendAction() override;
+ void ResumeAction() override;
+ void TerminateProcessing() override;
+ void ActionCompleted(ErrorCode error_code) override;
+ std::string Type() const override;
+ static std::string StaticType();
+ typedef ActionTraits<CleanupPreviousUpdateAction>::InputObjectType
+ InputObjectType;
+ typedef ActionTraits<CleanupPreviousUpdateAction>::OutputObjectType
+ OutputObjectType;
+
+ private:
+ PrefsInterface* prefs_;
+ BootControlInterface* boot_control_;
+ android::snapshot::ISnapshotManager* snapshot_;
+ CleanupPreviousUpdateActionDelegateInterface* delegate_;
+ std::unique_ptr<android::snapshot::AutoDevice> metadata_device_;
+ bool running_{false};
+ bool cancel_failed_{false};
+ unsigned int last_percentage_{0};
+ android::snapshot::ISnapshotMergeStats* merge_stats_;
+ brillo::MessageLoop::TaskId scheduled_task_{brillo::MessageLoop::kTaskIdNull};
+
+ // Helpers for task management.
+ void AcknowledgeTaskExecuted();
+ void CheckTaskScheduled(std::string_view name);
+
+ void StopActionInternal();
+ void StartActionInternal();
+ void ScheduleWaitBootCompleted();
+ void WaitBootCompletedOrSchedule();
+ void ScheduleWaitMarkBootSuccessful();
+ void CheckSlotMarkedSuccessfulOrSchedule();
+ void ScheduleWaitForMerge();
+ void WaitForMergeOrSchedule();
+ void InitiateMergeAndWait();
+ void ReportMergeStats();
+
+ // Callbacks to ProcessUpdateState.
+ bool OnMergePercentageUpdate();
+ bool BeforeCancel();
+};
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_AOSP_CLEANUP_PREVIOUS_UPDATE_ACTION_H_
diff --git a/aosp/daemon_android.cc b/aosp/daemon_android.cc
new file mode 100644
index 0000000..c102e3b
--- /dev/null
+++ b/aosp/daemon_android.cc
@@ -0,0 +1,74 @@
+//
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/aosp/daemon_android.h"
+
+#include <sysexits.h>
+
+#include <binderwrapper/binder_wrapper.h>
+
+#include "update_engine/aosp/daemon_state_android.h"
+
+using std::unique_ptr;
+
+namespace chromeos_update_engine {
+
+unique_ptr<DaemonBase> DaemonBase::CreateInstance() {
+ return std::make_unique<DaemonAndroid>();
+}
+
+int DaemonAndroid::OnInit() {
+ // Register the |subprocess_| singleton with this Daemon as the signal
+ // handler.
+ subprocess_.Init(this);
+
+ int exit_code = brillo::Daemon::OnInit();
+ if (exit_code != EX_OK)
+ return exit_code;
+
+ android::BinderWrapper::Create();
+ binder_watcher_.Init();
+
+ DaemonStateAndroid* daemon_state_android = new DaemonStateAndroid();
+ daemon_state_.reset(daemon_state_android);
+ LOG_IF(ERROR, !daemon_state_android->Initialize())
+ << "Failed to initialize system state.";
+
+ auto binder_wrapper = android::BinderWrapper::Get();
+
+ // Create the Binder Service.
+ binder_service_ = new BinderUpdateEngineAndroidService{
+ daemon_state_android->service_delegate()};
+ if (!binder_wrapper->RegisterService(binder_service_->ServiceName(),
+ binder_service_)) {
+ LOG(ERROR) << "Failed to register binder service.";
+ }
+ daemon_state_->AddObserver(binder_service_.get());
+
+ // Create the stable binder service.
+ stable_binder_service_ = new BinderUpdateEngineAndroidStableService{
+ daemon_state_android->service_delegate()};
+ if (!binder_wrapper->RegisterService(stable_binder_service_->ServiceName(),
+ stable_binder_service_)) {
+ LOG(ERROR) << "Failed to register stable binder service.";
+ }
+ daemon_state_->AddObserver(stable_binder_service_.get());
+
+ daemon_state_->StartUpdater();
+ return EX_OK;
+}
+
+} // namespace chromeos_update_engine
diff --git a/aosp/daemon_android.h b/aosp/daemon_android.h
new file mode 100644
index 0000000..38a8689
--- /dev/null
+++ b/aosp/daemon_android.h
@@ -0,0 +1,58 @@
+//
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_AOSP_DAEMON_ANDROID_H_
+#define UPDATE_ENGINE_AOSP_DAEMON_ANDROID_H_
+
+#include <memory>
+
+#include <brillo/binder_watcher.h>
+
+#include "update_engine/aosp/binder_service_android.h"
+#include "update_engine/aosp/binder_service_stable_android.h"
+#include "update_engine/common/daemon_base.h"
+#include "update_engine/common/daemon_state_interface.h"
+#include "update_engine/common/subprocess.h"
+
+namespace chromeos_update_engine {
+
+class DaemonAndroid : public DaemonBase {
+ public:
+ DaemonAndroid() = default;
+
+ protected:
+ int OnInit() override;
+
+ private:
+ // The Subprocess singleton class requires a |brillo::MessageLoop| in the
+ // current thread, so we need to initialize it from this class instead of
+ // the main() function.
+ Subprocess subprocess_;
+
+ brillo::BinderWatcher binder_watcher_;
+ android::sp<BinderUpdateEngineAndroidService> binder_service_;
+ android::sp<BinderUpdateEngineAndroidStableService> stable_binder_service_;
+
+ // The daemon state with all the required daemon classes for the configured
+ // platform.
+ std::unique_ptr<DaemonStateInterface> daemon_state_;
+
+ DISALLOW_COPY_AND_ASSIGN(DaemonAndroid);
+};
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_AOSP_DAEMON_ANDROID_H_
diff --git a/aosp/daemon_state_android.cc b/aosp/daemon_state_android.cc
new file mode 100644
index 0000000..9bdd175
--- /dev/null
+++ b/aosp/daemon_state_android.cc
@@ -0,0 +1,92 @@
+//
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/aosp/daemon_state_android.h"
+
+#include <base/logging.h>
+
+#include "update_engine/aosp/update_attempter_android.h"
+#include "update_engine/common/boot_control.h"
+#include "update_engine/common/boot_control_stub.h"
+#include "update_engine/common/hardware.h"
+#include "update_engine/common/prefs.h"
+
+namespace chromeos_update_engine {
+
+bool DaemonStateAndroid::Initialize() {
+ boot_control_ = boot_control::CreateBootControl();
+ if (!boot_control_) {
+ LOG(WARNING) << "Unable to create BootControl instance, using stub "
+ << "instead. All update attempts will fail.";
+ boot_control_.reset(new BootControlStub());
+ }
+
+ hardware_ = hardware::CreateHardware();
+ if (!hardware_) {
+ LOG(ERROR) << "Error initializing the HardwareInterface.";
+ return false;
+ }
+
+ LOG_IF(INFO, !hardware_->IsNormalBootMode()) << "Booted in dev mode.";
+ LOG_IF(INFO, !hardware_->IsOfficialBuild()) << "Booted non-official build.";
+
+ // Initialize prefs.
+ base::FilePath non_volatile_path;
+ if (!hardware_->GetNonVolatileDirectory(&non_volatile_path)) {
+ prefs_.reset(new MemoryPrefs());
+ LOG(WARNING)
+ << "Could not get a non-volatile directory, fall back to memory prefs";
+ } else {
+ Prefs* prefs = new Prefs();
+ prefs_.reset(prefs);
+ if (!prefs->Init(non_volatile_path.Append(kPrefsSubDirectory))) {
+ LOG(ERROR) << "Failed to initialize preferences.";
+ return false;
+ }
+ }
+
+ // The CertificateChecker singleton is used by the update attempter.
+ certificate_checker_.reset(
+ new CertificateChecker(prefs_.get(), &openssl_wrapper_));
+ certificate_checker_->Init();
+
+ // Initialize the UpdateAttempter before the UpdateManager.
+ update_attempter_.reset(new UpdateAttempterAndroid(
+ this, prefs_.get(), boot_control_.get(), hardware_.get()));
+
+ return true;
+}
+
+bool DaemonStateAndroid::StartUpdater() {
+ // The DaemonState in Android is a passive daemon. It will only start applying
+ // an update when instructed to do so from the exposed binder API.
+ update_attempter_->Init();
+ return true;
+}
+
+void DaemonStateAndroid::AddObserver(ServiceObserverInterface* observer) {
+ service_observers_.insert(observer);
+}
+
+void DaemonStateAndroid::RemoveObserver(ServiceObserverInterface* observer) {
+ service_observers_.erase(observer);
+}
+
+ServiceDelegateAndroidInterface* DaemonStateAndroid::service_delegate() {
+ return update_attempter_.get();
+}
+
+} // namespace chromeos_update_engine
diff --git a/aosp/daemon_state_android.h b/aosp/daemon_state_android.h
new file mode 100644
index 0000000..dea3a23
--- /dev/null
+++ b/aosp/daemon_state_android.h
@@ -0,0 +1,76 @@
+//
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_AOSP_DAEMON_STATE_ANDROID_H_
+#define UPDATE_ENGINE_AOSP_DAEMON_STATE_ANDROID_H_
+
+#include <memory>
+#include <set>
+
+#include "update_engine/aosp/service_delegate_android_interface.h"
+#include "update_engine/aosp/update_attempter_android.h"
+#include "update_engine/certificate_checker.h"
+#include "update_engine/common/boot_control_interface.h"
+#include "update_engine/common/daemon_state_interface.h"
+#include "update_engine/common/hardware_interface.h"
+#include "update_engine/common/prefs_interface.h"
+#include "update_engine/common/service_observer_interface.h"
+
+namespace chromeos_update_engine {
+
+class DaemonStateAndroid : public DaemonStateInterface {
+ public:
+ DaemonStateAndroid() = default;
+ ~DaemonStateAndroid() override = default;
+
+ bool Initialize();
+
+ // DaemonStateInterface overrides.
+ bool StartUpdater() override;
+ void AddObserver(ServiceObserverInterface* observer) override;
+ void RemoveObserver(ServiceObserverInterface* observer) override;
+
+ const std::set<ServiceObserverInterface*>& service_observers() override {
+ return service_observers_;
+ }
+
+ // Return a pointer to the service delegate.
+ ServiceDelegateAndroidInterface* service_delegate();
+
+ protected:
+ std::set<ServiceObserverInterface*> service_observers_;
+
+ // Interface for the boot control functions.
+ std::unique_ptr<BootControlInterface> boot_control_;
+
+ // Interface for the hardware functions.
+ std::unique_ptr<HardwareInterface> hardware_;
+
+ // Interface for persisted store.
+ std::unique_ptr<PrefsInterface> prefs_;
+
+ // The main class handling the updates.
+ std::unique_ptr<UpdateAttempterAndroid> update_attempter_;
+
+ // OpenSSLWrapper and CertificateChecker used for checking changes in SSL
+ // certificates.
+ OpenSSLWrapper openssl_wrapper_;
+ std::unique_ptr<CertificateChecker> certificate_checker_;
+};
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_AOSP_DAEMON_STATE_ANDROID_H_
diff --git a/aosp/dynamic_partition_control_android.cc b/aosp/dynamic_partition_control_android.cc
new file mode 100644
index 0000000..d461807
--- /dev/null
+++ b/aosp/dynamic_partition_control_android.cc
@@ -0,0 +1,1274 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/aosp/dynamic_partition_control_android.h"
+
+#include <chrono> // NOLINT(build/c++11) - using libsnapshot / liblp API
+#include <cstdint>
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <base/files/file_util.h>
+#include <base/logging.h>
+#include <base/strings/string_util.h>
+#include <bootloader_message/bootloader_message.h>
+#include <fs_mgr.h>
+#include <fs_mgr_dm_linear.h>
+#include <fs_mgr_overlayfs.h>
+#include <libavb/libavb.h>
+#include <libdm/dm.h>
+#include <liblp/liblp.h>
+#include <libsnapshot/cow_writer.h>
+#include <libsnapshot/snapshot.h>
+#include <libsnapshot/snapshot_stub.h>
+
+#include "update_engine/aosp/cleanup_previous_update_action.h"
+#include "update_engine/aosp/dynamic_partition_utils.h"
+#include "update_engine/common/boot_control_interface.h"
+#include "update_engine/common/utils.h"
+#include "update_engine/payload_consumer/delta_performer.h"
+
+using android::base::GetBoolProperty;
+using android::base::GetProperty;
+using android::base::Join;
+using android::dm::DeviceMapper;
+using android::dm::DmDeviceState;
+using android::fs_mgr::CreateLogicalPartition;
+using android::fs_mgr::CreateLogicalPartitionParams;
+using android::fs_mgr::DestroyLogicalPartition;
+using android::fs_mgr::Fstab;
+using android::fs_mgr::MetadataBuilder;
+using android::fs_mgr::Partition;
+using android::fs_mgr::PartitionOpener;
+using android::fs_mgr::SlotSuffixForSlotNumber;
+using android::snapshot::OptimizeSourceCopyOperation;
+using android::snapshot::Return;
+using android::snapshot::SnapshotManager;
+using android::snapshot::SnapshotManagerStub;
+using android::snapshot::UpdateState;
+
+namespace chromeos_update_engine {
+
+constexpr char kUseDynamicPartitions[] = "ro.boot.dynamic_partitions";
+constexpr char kRetrfoitDynamicPartitions[] =
+ "ro.boot.dynamic_partitions_retrofit";
+constexpr char kVirtualAbEnabled[] = "ro.virtual_ab.enabled";
+constexpr char kVirtualAbRetrofit[] = "ro.virtual_ab.retrofit";
+constexpr char kVirtualAbCompressionEnabled[] =
+ "ro.virtual_ab.compression.enabled";
+
+// Currently, android doesn't have a retrofit prop for VAB Compression. However,
+// struct FeatureFlag forces us to determine if a feature is 'retrofit'. So this
+// is here just to simplify code. Replace it with real retrofit prop name once
+// there is one.
+constexpr char kVirtualAbCompressionRetrofit[] = "";
+constexpr char kPostinstallFstabPrefix[] = "ro.postinstall.fstab.prefix";
+// Map timeout for dynamic partitions.
+constexpr std::chrono::milliseconds kMapTimeout{1000};
+// Map timeout for dynamic partitions with snapshots. Since several devices
+// needs to be mapped, this timeout is longer than |kMapTimeout|.
+constexpr std::chrono::milliseconds kMapSnapshotTimeout{5000};
+
+DynamicPartitionControlAndroid::~DynamicPartitionControlAndroid() {
+ Cleanup();
+}
+
+static FeatureFlag GetFeatureFlag(const char* enable_prop,
+ const char* retrofit_prop) {
+ // Default retrofit to false if retrofit_prop is empty.
+ bool retrofit = retrofit_prop && retrofit_prop[0] != '\0' &&
+ GetBoolProperty(retrofit_prop, false);
+ bool enabled = GetBoolProperty(enable_prop, false);
+ if (retrofit && !enabled) {
+ LOG(ERROR) << retrofit_prop << " is true but " << enable_prop
+ << " is not. These sysprops are inconsistent. Assume that "
+ << enable_prop << " is true from now on.";
+ }
+ if (retrofit) {
+ return FeatureFlag(FeatureFlag::Value::RETROFIT);
+ }
+ if (enabled) {
+ return FeatureFlag(FeatureFlag::Value::LAUNCH);
+ }
+ return FeatureFlag(FeatureFlag::Value::NONE);
+}
+
+DynamicPartitionControlAndroid::DynamicPartitionControlAndroid()
+ : dynamic_partitions_(
+ GetFeatureFlag(kUseDynamicPartitions, kRetrfoitDynamicPartitions)),
+ virtual_ab_(GetFeatureFlag(kVirtualAbEnabled, kVirtualAbRetrofit)),
+ virtual_ab_compression_(GetFeatureFlag(kVirtualAbCompressionEnabled,
+ kVirtualAbCompressionRetrofit)) {
+ if (GetVirtualAbFeatureFlag().IsEnabled()) {
+ snapshot_ = SnapshotManager::New();
+ } else {
+ snapshot_ = SnapshotManagerStub::New();
+ }
+ CHECK(snapshot_ != nullptr) << "Cannot initialize SnapshotManager.";
+}
+
+FeatureFlag DynamicPartitionControlAndroid::GetDynamicPartitionsFeatureFlag() {
+ return dynamic_partitions_;
+}
+
+FeatureFlag DynamicPartitionControlAndroid::GetVirtualAbFeatureFlag() {
+ return virtual_ab_;
+}
+
+FeatureFlag
+DynamicPartitionControlAndroid::GetVirtualAbCompressionFeatureFlag() {
+ return virtual_ab_compression_;
+}
+
+bool DynamicPartitionControlAndroid::OptimizeOperation(
+ const std::string& partition_name,
+ const InstallOperation& operation,
+ InstallOperation* optimized) {
+ switch (operation.type()) {
+ case InstallOperation::SOURCE_COPY:
+ return target_supports_snapshot_ &&
+ GetVirtualAbFeatureFlag().IsEnabled() &&
+ mapped_devices_.count(partition_name +
+ SlotSuffixForSlotNumber(target_slot_)) > 0 &&
+ OptimizeSourceCopyOperation(operation, optimized);
+ break;
+ default:
+ break;
+ }
+ return false;
+}
+
+bool DynamicPartitionControlAndroid::MapPartitionInternal(
+ const std::string& super_device,
+ const std::string& target_partition_name,
+ uint32_t slot,
+ bool force_writable,
+ std::string* path) {
+ CreateLogicalPartitionParams params = {
+ .block_device = super_device,
+ .metadata_slot = slot,
+ .partition_name = target_partition_name,
+ .force_writable = force_writable,
+ };
+ bool success = false;
+ if (GetVirtualAbFeatureFlag().IsEnabled() && target_supports_snapshot_ &&
+ force_writable && ExpectMetadataMounted()) {
+ // Only target partitions are mapped with force_writable. On Virtual
+ // A/B devices, target partitions may overlap with source partitions, so
+ // they must be mapped with snapshot.
+ // One exception is when /metadata is not mounted. Fallback to
+ // CreateLogicalPartition as snapshots are not created in the first place.
+ params.timeout_ms = kMapSnapshotTimeout;
+ success = snapshot_->MapUpdateSnapshot(params, path);
+ } else {
+ params.timeout_ms = kMapTimeout;
+ success = CreateLogicalPartition(params, path);
+ }
+
+ if (!success) {
+ LOG(ERROR) << "Cannot map " << target_partition_name << " in "
+ << super_device << " on device mapper.";
+ return false;
+ }
+ LOG(INFO) << "Succesfully mapped " << target_partition_name
+ << " to device mapper (force_writable = " << force_writable
+ << "); device path at " << *path;
+ mapped_devices_.insert(target_partition_name);
+ return true;
+}
+
+bool DynamicPartitionControlAndroid::MapPartitionOnDeviceMapper(
+ const std::string& super_device,
+ const std::string& target_partition_name,
+ uint32_t slot,
+ bool force_writable,
+ std::string* path) {
+ DmDeviceState state = GetState(target_partition_name);
+ if (state == DmDeviceState::ACTIVE) {
+ if (mapped_devices_.find(target_partition_name) != mapped_devices_.end()) {
+ if (GetDmDevicePathByName(target_partition_name, path)) {
+ LOG(INFO) << target_partition_name
+ << " is mapped on device mapper: " << *path;
+ return true;
+ }
+ LOG(ERROR) << target_partition_name << " is mapped but path is unknown.";
+ return false;
+ }
+ // If target_partition_name is not in mapped_devices_ but state is ACTIVE,
+ // the device might be mapped incorrectly before. Attempt to unmap it.
+ // Note that for source partitions, if GetState() == ACTIVE, callers (e.g.
+ // BootControlAndroid) should not call MapPartitionOnDeviceMapper, but
+ // should directly call GetDmDevicePathByName.
+ if (!UnmapPartitionOnDeviceMapper(target_partition_name)) {
+ LOG(ERROR) << target_partition_name
+ << " is mapped before the update, and it cannot be unmapped.";
+ return false;
+ }
+ state = GetState(target_partition_name);
+ if (state != DmDeviceState::INVALID) {
+ LOG(ERROR) << target_partition_name << " is unmapped but state is "
+ << static_cast<std::underlying_type_t<DmDeviceState>>(state);
+ return false;
+ }
+ }
+ if (state == DmDeviceState::INVALID) {
+ return MapPartitionInternal(
+ super_device, target_partition_name, slot, force_writable, path);
+ }
+
+ LOG(ERROR) << target_partition_name
+ << " is mapped on device mapper but state is unknown: "
+ << static_cast<std::underlying_type_t<DmDeviceState>>(state);
+ return false;
+}
+
+bool DynamicPartitionControlAndroid::UnmapPartitionOnDeviceMapper(
+ const std::string& target_partition_name) {
+ if (DeviceMapper::Instance().GetState(target_partition_name) !=
+ DmDeviceState::INVALID) {
+ // Partitions at target slot on non-Virtual A/B devices are mapped as
+ // dm-linear. Also, on Virtual A/B devices, system_other may be mapped for
+ // preopt apps as dm-linear.
+ // Call DestroyLogicalPartition to handle these cases.
+ bool success = DestroyLogicalPartition(target_partition_name);
+
+ // On a Virtual A/B device, |target_partition_name| may be a leftover from
+ // a paused update. Clean up any underlying devices.
+ if (ExpectMetadataMounted()) {
+ success &= snapshot_->UnmapUpdateSnapshot(target_partition_name);
+ } else {
+ LOG(INFO) << "Skip UnmapUpdateSnapshot(" << target_partition_name
+ << ") because metadata is not mounted";
+ }
+
+ if (!success) {
+ LOG(ERROR) << "Cannot unmap " << target_partition_name
+ << " from device mapper.";
+ return false;
+ }
+ LOG(INFO) << "Successfully unmapped " << target_partition_name
+ << " from device mapper.";
+ }
+ mapped_devices_.erase(target_partition_name);
+ return true;
+}
+
+bool DynamicPartitionControlAndroid::UnmapAllPartitions() {
+ if (mapped_devices_.empty()) {
+ return false;
+ }
+ // UnmapPartitionOnDeviceMapper removes objects from mapped_devices_, hence
+ // a copy is needed for the loop.
+ std::set<std::string> mapped = mapped_devices_;
+ LOG(INFO) << "Destroying [" << Join(mapped, ", ") << "] from device mapper";
+ for (const auto& partition_name : mapped) {
+ ignore_result(UnmapPartitionOnDeviceMapper(partition_name));
+ }
+ return true;
+}
+
+void DynamicPartitionControlAndroid::Cleanup() {
+ UnmapAllPartitions();
+ metadata_device_.reset();
+}
+
+bool DynamicPartitionControlAndroid::DeviceExists(const std::string& path) {
+ return base::PathExists(base::FilePath(path));
+}
+
+android::dm::DmDeviceState DynamicPartitionControlAndroid::GetState(
+ const std::string& name) {
+ return DeviceMapper::Instance().GetState(name);
+}
+
+bool DynamicPartitionControlAndroid::GetDmDevicePathByName(
+ const std::string& name, std::string* path) {
+ return DeviceMapper::Instance().GetDmDevicePathByName(name, path);
+}
+
+std::unique_ptr<MetadataBuilder>
+DynamicPartitionControlAndroid::LoadMetadataBuilder(
+ const std::string& super_device, uint32_t slot) {
+ auto builder = MetadataBuilder::New(PartitionOpener(), super_device, slot);
+ if (builder == nullptr) {
+ LOG(WARNING) << "No metadata slot " << BootControlInterface::SlotName(slot)
+ << " in " << super_device;
+ return nullptr;
+ }
+ LOG(INFO) << "Loaded metadata from slot "
+ << BootControlInterface::SlotName(slot) << " in " << super_device;
+ return builder;
+}
+
+std::unique_ptr<MetadataBuilder>
+DynamicPartitionControlAndroid::LoadMetadataBuilder(
+ const std::string& super_device,
+ uint32_t source_slot,
+ uint32_t target_slot) {
+ bool always_keep_source_slot = !target_supports_snapshot_;
+ auto builder = MetadataBuilder::NewForUpdate(PartitionOpener(),
+ super_device,
+ source_slot,
+ target_slot,
+ always_keep_source_slot);
+ if (builder == nullptr) {
+ LOG(WARNING) << "No metadata slot "
+ << BootControlInterface::SlotName(source_slot) << " in "
+ << super_device;
+ return nullptr;
+ }
+ LOG(INFO) << "Created metadata for new update from slot "
+ << BootControlInterface::SlotName(source_slot) << " in "
+ << super_device;
+ return builder;
+}
+
+bool DynamicPartitionControlAndroid::StoreMetadata(
+ const std::string& super_device,
+ MetadataBuilder* builder,
+ uint32_t target_slot) {
+ auto metadata = builder->Export();
+ if (metadata == nullptr) {
+ LOG(ERROR) << "Cannot export metadata to slot "
+ << BootControlInterface::SlotName(target_slot) << " in "
+ << super_device;
+ return false;
+ }
+
+ if (GetDynamicPartitionsFeatureFlag().IsRetrofit()) {
+ if (!FlashPartitionTable(super_device, *metadata)) {
+ LOG(ERROR) << "Cannot write metadata to " << super_device;
+ return false;
+ }
+ LOG(INFO) << "Written metadata to " << super_device;
+ } else {
+ if (!UpdatePartitionTable(super_device, *metadata, target_slot)) {
+ LOG(ERROR) << "Cannot write metadata to slot "
+ << BootControlInterface::SlotName(target_slot) << " in "
+ << super_device;
+ return false;
+ }
+ LOG(INFO) << "Copied metadata to slot "
+ << BootControlInterface::SlotName(target_slot) << " in "
+ << super_device;
+ }
+
+ return true;
+}
+
+bool DynamicPartitionControlAndroid::GetDeviceDir(std::string* out) {
+ // We can't use fs_mgr to look up |partition_name| because fstab
+ // doesn't list every slot partition (it uses the slotselect option
+ // to mask the suffix).
+ //
+ // We can however assume that there's an entry for the /misc mount
+ // point and use that to get the device file for the misc
+ // partition. This helps us locate the disk that |partition_name|
+ // resides on. From there we'll assume that a by-name scheme is used
+ // so we can just replace the trailing "misc" by the given
+ // |partition_name| and suffix corresponding to |slot|, e.g.
+ //
+ // /dev/block/platform/soc.0/7824900.sdhci/by-name/misc ->
+ // /dev/block/platform/soc.0/7824900.sdhci/by-name/boot_a
+ //
+ // If needed, it's possible to relax the by-name assumption in the
+ // future by trawling /sys/block looking for the appropriate sibling
+ // of misc and then finding an entry in /dev matching the sysfs
+ // entry.
+
+ std::string err, misc_device = get_bootloader_message_blk_device(&err);
+ if (misc_device.empty()) {
+ LOG(ERROR) << "Unable to get misc block device: " << err;
+ return false;
+ }
+
+ if (!utils::IsSymlink(misc_device.c_str())) {
+ LOG(ERROR) << "Device file " << misc_device << " for /misc "
+ << "is not a symlink.";
+ return false;
+ }
+ *out = base::FilePath(misc_device).DirName().value();
+ return true;
+}
+
+bool DynamicPartitionControlAndroid::PreparePartitionsForUpdate(
+ uint32_t source_slot,
+ uint32_t target_slot,
+ const DeltaArchiveManifest& manifest,
+ bool update,
+ uint64_t* required_size) {
+ source_slot_ = source_slot;
+ target_slot_ = target_slot;
+ if (required_size != nullptr) {
+ *required_size = 0;
+ }
+
+ if (fs_mgr_overlayfs_is_setup()) {
+ // Non DAP devices can use overlayfs as well.
+ LOG(WARNING)
+ << "overlayfs overrides are active and can interfere with our "
+ "resources.\n"
+ << "run adb enable-verity to deactivate if required and try again.";
+ }
+
+ // If metadata is erased but not formatted, it is possible to not mount
+ // it in recovery. It is acceptable to skip mounting and choose fallback path
+ // (PrepareDynamicPartitionsForUpdate) when sideloading full OTAs.
+ TEST_AND_RETURN_FALSE(EnsureMetadataMounted() || IsRecovery());
+
+ if (update) {
+ TEST_AND_RETURN_FALSE(EraseSystemOtherAvbFooter(source_slot, target_slot));
+ }
+
+ if (!GetDynamicPartitionsFeatureFlag().IsEnabled()) {
+ return true;
+ }
+
+ if (target_slot == source_slot) {
+ LOG(ERROR) << "Cannot call PreparePartitionsForUpdate on current slot.";
+ return false;
+ }
+
+ if (!SetTargetBuildVars(manifest)) {
+ return false;
+ }
+
+ // Although the current build supports dynamic partitions, the given payload
+ // doesn't use it for target partitions. This could happen when applying a
+ // retrofit update. Skip updating the partition metadata for the target slot.
+ if (!is_target_dynamic_) {
+ return true;
+ }
+
+ if (!update)
+ return true;
+
+ bool delete_source = false;
+
+ if (GetVirtualAbFeatureFlag().IsEnabled()) {
+ // On Virtual A/B device, either CancelUpdate() or BeginUpdate() must be
+ // called before calling UnmapUpdateSnapshot.
+ // - If target_supports_snapshot_, PrepareSnapshotPartitionsForUpdate()
+ // calls BeginUpdate() which resets update state
+ // - If !target_supports_snapshot_ or PrepareSnapshotPartitionsForUpdate
+ // failed in recovery, explicitly CancelUpdate().
+ if (target_supports_snapshot_) {
+ if (PrepareSnapshotPartitionsForUpdate(
+ source_slot, target_slot, manifest, required_size)) {
+ return true;
+ }
+
+ // Virtual A/B device doing Virtual A/B update in Android mode must use
+ // snapshots.
+ if (!IsRecovery()) {
+ LOG(ERROR) << "PrepareSnapshotPartitionsForUpdate failed in Android "
+ << "mode";
+ return false;
+ }
+
+ delete_source = true;
+ LOG(INFO) << "PrepareSnapshotPartitionsForUpdate failed in recovery. "
+ << "Attempt to overwrite existing partitions if possible";
+ } else {
+ // Downgrading to an non-Virtual A/B build or is secondary OTA.
+ LOG(INFO) << "Using regular A/B on Virtual A/B because package disabled "
+ << "snapshots.";
+ }
+
+ // In recovery, if /metadata is not mounted, it is likely that metadata
+ // partition is erased and not formatted yet. After sideloading, when
+ // rebooting into the new version, init will erase metadata partition,
+ // hence the failure of CancelUpdate() can be ignored here.
+ // However, if metadata is mounted and CancelUpdate fails, sideloading
+ // should not proceed because during next boot, snapshots will overlay on
+ // the devices incorrectly.
+ if (ExpectMetadataMounted()) {
+ TEST_AND_RETURN_FALSE(snapshot_->CancelUpdate());
+ } else {
+ LOG(INFO) << "Skip canceling previous update because metadata is not "
+ << "mounted";
+ }
+ }
+
+ // TODO(xunchang) support partial update on non VAB enabled devices.
+ TEST_AND_RETURN_FALSE(PrepareDynamicPartitionsForUpdate(
+ source_slot, target_slot, manifest, delete_source));
+
+ if (required_size != nullptr) {
+ *required_size = 0;
+ }
+ return true;
+}
+
+bool DynamicPartitionControlAndroid::SetTargetBuildVars(
+ const DeltaArchiveManifest& manifest) {
+ // Precondition: current build supports dynamic partition.
+ CHECK(GetDynamicPartitionsFeatureFlag().IsEnabled());
+
+ bool is_target_dynamic =
+ !manifest.dynamic_partition_metadata().groups().empty();
+ bool target_supports_snapshot =
+ manifest.dynamic_partition_metadata().snapshot_enabled();
+
+ if (manifest.partial_update()) {
+ // Partial updates requires DAP. On partial updates that does not involve
+ // dynamic partitions, groups() can be empty, so also assume
+ // is_target_dynamic in this case. This assumption should be safe because we
+ // also check target_supports_snapshot below, which presumably also implies
+ // target build supports dynamic partition.
+ if (!is_target_dynamic) {
+ LOG(INFO) << "Assuming target build supports dynamic partitions for "
+ "partial updates.";
+ is_target_dynamic = true;
+ }
+
+ // Partial updates requires Virtual A/B. Double check that both current
+ // build and target build supports Virtual A/B.
+ if (!GetVirtualAbFeatureFlag().IsEnabled()) {
+ LOG(ERROR) << "Partial update cannot be applied on a device that does "
+ "not support snapshots.";
+ return false;
+ }
+ if (!target_supports_snapshot) {
+ LOG(ERROR) << "Cannot apply partial update to a build that does not "
+ "support snapshots.";
+ return false;
+ }
+ }
+
+ // Store the flags.
+ is_target_dynamic_ = is_target_dynamic;
+ // If !is_target_dynamic_, leave target_supports_snapshot_ unset because
+ // snapshots would not work without dynamic partition.
+ if (is_target_dynamic_) {
+ target_supports_snapshot_ = target_supports_snapshot;
+ }
+ return true;
+}
+
+namespace {
+// Try our best to erase AVB footer.
+class AvbFooterEraser {
+ public:
+ explicit AvbFooterEraser(const std::string& path) : path_(path) {}
+ bool Erase() {
+ // Try to mark the block device read-only. Ignore any
+ // failure since this won't work when passing regular files.
+ ignore_result(utils::SetBlockDeviceReadOnly(path_, false /* readonly */));
+
+ fd_.reset(new EintrSafeFileDescriptor());
+ int flags = O_WRONLY | O_TRUNC | O_CLOEXEC | O_SYNC;
+ TEST_AND_RETURN_FALSE(fd_->Open(path_.c_str(), flags));
+
+ // Need to write end-AVB_FOOTER_SIZE to end.
+ static_assert(AVB_FOOTER_SIZE > 0);
+ off64_t offset = fd_->Seek(-AVB_FOOTER_SIZE, SEEK_END);
+ TEST_AND_RETURN_FALSE_ERRNO(offset >= 0);
+ uint64_t write_size = AVB_FOOTER_SIZE;
+ LOG(INFO) << "Zeroing " << path_ << " @ [" << offset << ", "
+ << (offset + write_size) << "] (" << write_size << " bytes)";
+ brillo::Blob zeros(write_size);
+ TEST_AND_RETURN_FALSE(utils::WriteAll(fd_, zeros.data(), zeros.size()));
+ return true;
+ }
+ ~AvbFooterEraser() {
+ TEST_AND_RETURN(fd_ != nullptr && fd_->IsOpen());
+ if (!fd_->Close()) {
+ LOG(WARNING) << "Failed to close fd for " << path_;
+ }
+ }
+
+ private:
+ std::string path_;
+ FileDescriptorPtr fd_;
+};
+
+} // namespace
+
+std::optional<bool>
+DynamicPartitionControlAndroid::IsAvbEnabledOnSystemOther() {
+ auto prefix = GetProperty(kPostinstallFstabPrefix, "");
+ if (prefix.empty()) {
+ LOG(WARNING) << "Cannot get " << kPostinstallFstabPrefix;
+ return std::nullopt;
+ }
+ auto path = base::FilePath(prefix).Append("etc/fstab.postinstall").value();
+ return IsAvbEnabledInFstab(path);
+}
+
+std::optional<bool> DynamicPartitionControlAndroid::IsAvbEnabledInFstab(
+ const std::string& path) {
+ Fstab fstab;
+ if (!ReadFstabFromFile(path, &fstab)) {
+ PLOG(WARNING) << "Cannot read fstab from " << path;
+ if (errno == ENOENT) {
+ return false;
+ }
+ return std::nullopt;
+ }
+ for (const auto& entry : fstab) {
+ if (!entry.avb_keys.empty()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool DynamicPartitionControlAndroid::GetSystemOtherPath(
+ uint32_t source_slot,
+ uint32_t target_slot,
+ const std::string& partition_name_suffix,
+ std::string* path,
+ bool* should_unmap) {
+ path->clear();
+ *should_unmap = false;
+
+ // Check that AVB is enabled on system_other before erasing.
+ auto has_avb = IsAvbEnabledOnSystemOther();
+ TEST_AND_RETURN_FALSE(has_avb.has_value());
+ if (!has_avb.value()) {
+ LOG(INFO) << "AVB is not enabled on system_other. Skip erasing.";
+ return true;
+ }
+
+ if (!IsRecovery()) {
+ // Found unexpected avb_keys for system_other on devices retrofitting
+ // dynamic partitions. Previous crash in update_engine may leave logical
+ // partitions mapped on physical system_other partition. It is difficult to
+ // handle these cases. Just fail.
+ if (GetDynamicPartitionsFeatureFlag().IsRetrofit()) {
+ LOG(ERROR) << "Cannot erase AVB footer on system_other on devices with "
+ << "retrofit dynamic partitions. They should not have AVB "
+ << "enabled on system_other.";
+ return false;
+ }
+ }
+
+ std::string device_dir_str;
+ TEST_AND_RETURN_FALSE(GetDeviceDir(&device_dir_str));
+ base::FilePath device_dir(device_dir_str);
+
+ // On devices without dynamic partition, search for static partitions.
+ if (!GetDynamicPartitionsFeatureFlag().IsEnabled()) {
+ *path = device_dir.Append(partition_name_suffix).value();
+ TEST_AND_RETURN_FALSE(DeviceExists(*path));
+ return true;
+ }
+
+ auto source_super_device =
+ device_dir.Append(GetSuperPartitionName(source_slot)).value();
+
+ auto builder = LoadMetadataBuilder(source_super_device, source_slot);
+ if (builder == nullptr) {
+ if (IsRecovery()) {
+ // It might be corrupted for some reason. It should still be able to
+ // sideload.
+ LOG(WARNING) << "Super partition metadata cannot be read from the source "
+ << "slot, skip erasing.";
+ return true;
+ } else {
+ // Device has booted into Android mode, indicating that the super
+ // partition metadata should be there.
+ LOG(ERROR) << "Super partition metadata cannot be read from the source "
+ << "slot. This is unexpected on devices with dynamic "
+ << "partitions enabled.";
+ return false;
+ }
+ }
+ auto p = builder->FindPartition(partition_name_suffix);
+ if (p == nullptr) {
+ // If the source slot is flashed without system_other, it does not exist
+ // in super partition metadata at source slot. It is safe to skip it.
+ LOG(INFO) << "Can't find " << partition_name_suffix
+ << " in metadata source slot, skip erasing.";
+ return true;
+ }
+ // System_other created by flashing tools should be erased.
+ // If partition is created by update_engine (via NewForUpdate), it is a
+ // left-over partition from the previous update and does not contain
+ // system_other, hence there is no need to erase.
+ // Note the reverse is not necessary true. If the flag is not set, we don't
+ // know if the partition is created by update_engine or by flashing tools
+ // because older versions of super partition metadata does not contain this
+ // flag. It is okay to erase the AVB footer anyways.
+ if (p->attributes() & LP_PARTITION_ATTR_UPDATED) {
+ LOG(INFO) << partition_name_suffix
+ << " does not contain system_other, skip erasing.";
+ return true;
+ }
+
+ if (p->size() < AVB_FOOTER_SIZE) {
+ LOG(INFO) << partition_name_suffix << " has length " << p->size()
+ << "( < AVB_FOOTER_SIZE " << AVB_FOOTER_SIZE
+ << "), skip erasing.";
+ return true;
+ }
+
+ // Delete any pre-existing device with name |partition_name_suffix| and
+ // also remove it from |mapped_devices_|.
+ // In recovery, metadata might not be mounted, and
+ // UnmapPartitionOnDeviceMapper might fail. However,
+ // it is unusual that system_other has already been mapped. Hence, just skip.
+ TEST_AND_RETURN_FALSE(UnmapPartitionOnDeviceMapper(partition_name_suffix));
+ // Use CreateLogicalPartition directly to avoid mapping with existing
+ // snapshots.
+ CreateLogicalPartitionParams params = {
+ .block_device = source_super_device,
+ .metadata_slot = source_slot,
+ .partition_name = partition_name_suffix,
+ .force_writable = true,
+ .timeout_ms = kMapTimeout,
+ };
+ TEST_AND_RETURN_FALSE(CreateLogicalPartition(params, path));
+ *should_unmap = true;
+ return true;
+}
+
+bool DynamicPartitionControlAndroid::EraseSystemOtherAvbFooter(
+ uint32_t source_slot, uint32_t target_slot) {
+ LOG(INFO) << "Erasing AVB footer of system_other partition before update.";
+
+ const std::string target_suffix = SlotSuffixForSlotNumber(target_slot);
+ const std::string partition_name_suffix = "system" + target_suffix;
+
+ std::string path;
+ bool should_unmap = false;
+
+ TEST_AND_RETURN_FALSE(GetSystemOtherPath(
+ source_slot, target_slot, partition_name_suffix, &path, &should_unmap));
+
+ if (path.empty()) {
+ return true;
+ }
+
+ bool ret = AvbFooterEraser(path).Erase();
+
+ // Delete |partition_name_suffix| from device mapper and from
+ // |mapped_devices_| again so that it does not interfere with update process.
+ // In recovery, metadata might not be mounted, and
+ // UnmapPartitionOnDeviceMapper might fail. However, DestroyLogicalPartition
+ // should be called. If DestroyLogicalPartition does fail, it is still okay
+ // to skip the error here and let Prepare*() fail later.
+ if (should_unmap) {
+ TEST_AND_RETURN_FALSE(UnmapPartitionOnDeviceMapper(partition_name_suffix));
+ }
+
+ return ret;
+}
+
+bool DynamicPartitionControlAndroid::PrepareDynamicPartitionsForUpdate(
+ uint32_t source_slot,
+ uint32_t target_slot,
+ const DeltaArchiveManifest& manifest,
+ bool delete_source) {
+ const std::string target_suffix = SlotSuffixForSlotNumber(target_slot);
+
+ // Unmap all the target dynamic partitions because they would become
+ // inconsistent with the new metadata.
+ for (const auto& group : manifest.dynamic_partition_metadata().groups()) {
+ for (const auto& partition_name : group.partition_names()) {
+ if (!UnmapPartitionOnDeviceMapper(partition_name + target_suffix)) {
+ return false;
+ }
+ }
+ }
+
+ std::string device_dir_str;
+ if (!GetDeviceDir(&device_dir_str)) {
+ return false;
+ }
+ base::FilePath device_dir(device_dir_str);
+ auto source_device =
+ device_dir.Append(GetSuperPartitionName(source_slot)).value();
+
+ auto builder = LoadMetadataBuilder(source_device, source_slot, target_slot);
+ if (builder == nullptr) {
+ LOG(ERROR) << "No metadata at "
+ << BootControlInterface::SlotName(source_slot);
+ return false;
+ }
+
+ if (delete_source) {
+ TEST_AND_RETURN_FALSE(
+ DeleteSourcePartitions(builder.get(), source_slot, manifest));
+ }
+
+ if (!UpdatePartitionMetadata(builder.get(), target_slot, manifest)) {
+ return false;
+ }
+
+ auto target_device =
+ device_dir.Append(GetSuperPartitionName(target_slot)).value();
+ return StoreMetadata(target_device, builder.get(), target_slot);
+}
+
+bool DynamicPartitionControlAndroid::PrepareSnapshotPartitionsForUpdate(
+ uint32_t source_slot,
+ uint32_t target_slot,
+ const DeltaArchiveManifest& manifest,
+ uint64_t* required_size) {
+ TEST_AND_RETURN_FALSE(ExpectMetadataMounted());
+ if (!snapshot_->BeginUpdate()) {
+ LOG(ERROR) << "Cannot begin new update.";
+ return false;
+ }
+ auto ret = snapshot_->CreateUpdateSnapshots(manifest);
+ if (!ret) {
+ LOG(ERROR) << "Cannot create update snapshots: " << ret.string();
+ if (required_size != nullptr &&
+ ret.error_code() == Return::ErrorCode::NO_SPACE) {
+ *required_size = ret.required_size();
+ }
+ return false;
+ }
+ return true;
+}
+
+std::string DynamicPartitionControlAndroid::GetSuperPartitionName(
+ uint32_t slot) {
+ return fs_mgr_get_super_partition_name(slot);
+}
+
+bool DynamicPartitionControlAndroid::UpdatePartitionMetadata(
+ MetadataBuilder* builder,
+ uint32_t target_slot,
+ const DeltaArchiveManifest& manifest) {
+ // Check preconditions.
+ CHECK(!GetVirtualAbFeatureFlag().IsEnabled() || IsRecovery())
+ << "UpdatePartitionMetadata is called on a Virtual A/B device "
+ "but source partitions is not deleted. This is not allowed.";
+
+ // If applying downgrade from Virtual A/B to non-Virtual A/B, the left-over
+ // COW group needs to be deleted to ensure there are enough space to create
+ // target partitions.
+ builder->RemoveGroupAndPartitions(android::snapshot::kCowGroupName);
+
+ const std::string target_suffix = SlotSuffixForSlotNumber(target_slot);
+ DeleteGroupsWithSuffix(builder, target_suffix);
+
+ uint64_t total_size = 0;
+ for (const auto& group : manifest.dynamic_partition_metadata().groups()) {
+ total_size += group.size();
+ }
+
+ std::string expr;
+ uint64_t allocatable_space = builder->AllocatableSpace();
+ // On device retrofitting dynamic partitions, allocatable_space = super.
+ // On device launching dynamic partitions w/o VAB,
+ // allocatable_space = super / 2.
+ // On device launching dynamic partitions with VAB, allocatable_space = super.
+ if (!GetDynamicPartitionsFeatureFlag().IsRetrofit() &&
+ !GetVirtualAbFeatureFlag().IsEnabled()) {
+ allocatable_space /= 2;
+ expr = "half of ";
+ }
+ if (total_size > allocatable_space) {
+ LOG(ERROR) << "The maximum size of all groups with suffix " << target_suffix
+ << " (" << total_size << ") has exceeded " << expr
+ << "allocatable space for dynamic partitions "
+ << allocatable_space << ".";
+ return false;
+ }
+
+ // name of partition(e.g. "system") -> size in bytes
+ std::map<std::string, uint64_t> partition_sizes;
+ for (const auto& partition : manifest.partitions()) {
+ partition_sizes.emplace(partition.partition_name(),
+ partition.new_partition_info().size());
+ }
+
+ for (const auto& group : manifest.dynamic_partition_metadata().groups()) {
+ auto group_name_suffix = group.name() + target_suffix;
+ if (!builder->AddGroup(group_name_suffix, group.size())) {
+ LOG(ERROR) << "Cannot add group " << group_name_suffix << " with size "
+ << group.size();
+ return false;
+ }
+ LOG(INFO) << "Added group " << group_name_suffix << " with size "
+ << group.size();
+
+ for (const auto& partition_name : group.partition_names()) {
+ auto partition_sizes_it = partition_sizes.find(partition_name);
+ if (partition_sizes_it == partition_sizes.end()) {
+ // TODO(tbao): Support auto-filling partition info for framework-only
+ // OTA.
+ LOG(ERROR) << "dynamic_partition_metadata contains partition "
+ << partition_name << " but it is not part of the manifest. "
+ << "This is not supported.";
+ return false;
+ }
+ uint64_t partition_size = partition_sizes_it->second;
+
+ auto partition_name_suffix = partition_name + target_suffix;
+ Partition* p = builder->AddPartition(
+ partition_name_suffix, group_name_suffix, LP_PARTITION_ATTR_READONLY);
+ if (!p) {
+ LOG(ERROR) << "Cannot add partition " << partition_name_suffix
+ << " to group " << group_name_suffix;
+ return false;
+ }
+ if (!builder->ResizePartition(p, partition_size)) {
+ LOG(ERROR) << "Cannot resize partition " << partition_name_suffix
+ << " to size " << partition_size << ". Not enough space?";
+ return false;
+ }
+ LOG(INFO) << "Added partition " << partition_name_suffix << " to group "
+ << group_name_suffix << " with size " << partition_size;
+ }
+ }
+
+ return true;
+}
+
+bool DynamicPartitionControlAndroid::FinishUpdate(bool powerwash_required) {
+ if (ExpectMetadataMounted()) {
+ if (snapshot_->GetUpdateState() == UpdateState::Initiated) {
+ LOG(INFO) << "Snapshot writes are done.";
+ return snapshot_->FinishedSnapshotWrites(powerwash_required);
+ }
+ } else {
+ LOG(INFO) << "Skip FinishedSnapshotWrites() because /metadata is not "
+ << "mounted";
+ }
+ return true;
+}
+
+bool DynamicPartitionControlAndroid::GetPartitionDevice(
+ const std::string& partition_name,
+ uint32_t slot,
+ uint32_t current_slot,
+ bool not_in_payload,
+ std::string* device,
+ bool* is_dynamic) {
+ const auto& partition_name_suffix =
+ partition_name + SlotSuffixForSlotNumber(slot);
+ std::string device_dir_str;
+ TEST_AND_RETURN_FALSE(GetDeviceDir(&device_dir_str));
+ base::FilePath device_dir(device_dir_str);
+
+ if (is_dynamic) {
+ *is_dynamic = false;
+ }
+
+ // When looking up target partition devices, treat them as static if the
+ // current payload doesn't encode them as dynamic partitions. This may happen
+ // when applying a retrofit update on top of a dynamic-partitions-enabled
+ // build.
+ if (GetDynamicPartitionsFeatureFlag().IsEnabled() &&
+ (slot == current_slot || is_target_dynamic_)) {
+ switch (GetDynamicPartitionDevice(device_dir,
+ partition_name_suffix,
+ slot,
+ current_slot,
+ not_in_payload,
+ device)) {
+ case DynamicPartitionDeviceStatus::SUCCESS:
+ if (is_dynamic) {
+ *is_dynamic = true;
+ }
+ return true;
+ case DynamicPartitionDeviceStatus::TRY_STATIC:
+ break;
+ case DynamicPartitionDeviceStatus::ERROR: // fallthrough
+ default:
+ return false;
+ }
+ }
+ base::FilePath path = device_dir.Append(partition_name_suffix);
+ if (!DeviceExists(path.value())) {
+ LOG(ERROR) << "Device file " << path.value() << " does not exist.";
+ return false;
+ }
+
+ *device = path.value();
+ return true;
+}
+
+bool DynamicPartitionControlAndroid::GetPartitionDevice(
+ const std::string& partition_name,
+ uint32_t slot,
+ uint32_t current_slot,
+ std::string* device) {
+ return GetPartitionDevice(
+ partition_name, slot, current_slot, false, device, nullptr);
+}
+
+bool DynamicPartitionControlAndroid::IsSuperBlockDevice(
+ const base::FilePath& device_dir,
+ uint32_t current_slot,
+ const std::string& partition_name_suffix) {
+ std::string source_device =
+ device_dir.Append(GetSuperPartitionName(current_slot)).value();
+ auto source_metadata = LoadMetadataBuilder(source_device, current_slot);
+ return source_metadata->HasBlockDevice(partition_name_suffix);
+}
+
+DynamicPartitionControlAndroid::DynamicPartitionDeviceStatus
+DynamicPartitionControlAndroid::GetDynamicPartitionDevice(
+ const base::FilePath& device_dir,
+ const std::string& partition_name_suffix,
+ uint32_t slot,
+ uint32_t current_slot,
+ bool not_in_payload,
+ std::string* device) {
+ std::string super_device =
+ device_dir.Append(GetSuperPartitionName(slot)).value();
+
+ auto builder = LoadMetadataBuilder(super_device, slot);
+ if (builder == nullptr) {
+ LOG(ERROR) << "No metadata in slot "
+ << BootControlInterface::SlotName(slot);
+ return DynamicPartitionDeviceStatus::ERROR;
+ }
+ if (builder->FindPartition(partition_name_suffix) == nullptr) {
+ LOG(INFO) << partition_name_suffix
+ << " is not in super partition metadata.";
+
+ if (IsSuperBlockDevice(device_dir, current_slot, partition_name_suffix)) {
+ LOG(ERROR) << "The static partition " << partition_name_suffix
+ << " is a block device for current metadata."
+ << "It cannot be used as a logical partition.";
+ return DynamicPartitionDeviceStatus::ERROR;
+ }
+
+ return DynamicPartitionDeviceStatus::TRY_STATIC;
+ }
+
+ if (slot == current_slot) {
+ if (GetState(partition_name_suffix) != DmDeviceState::ACTIVE) {
+ LOG(WARNING) << partition_name_suffix << " is at current slot but it is "
+ << "not mapped. Now try to map it.";
+ } else {
+ if (GetDmDevicePathByName(partition_name_suffix, device)) {
+ LOG(INFO) << partition_name_suffix
+ << " is mapped on device mapper: " << *device;
+ return DynamicPartitionDeviceStatus::SUCCESS;
+ }
+ LOG(ERROR) << partition_name_suffix << "is mapped but path is unknown.";
+ return DynamicPartitionDeviceStatus::ERROR;
+ }
+ }
+
+ bool force_writable = (slot != current_slot) && !not_in_payload;
+ if (MapPartitionOnDeviceMapper(
+ super_device, partition_name_suffix, slot, force_writable, device)) {
+ return DynamicPartitionDeviceStatus::SUCCESS;
+ }
+ return DynamicPartitionDeviceStatus::ERROR;
+}
+
+void DynamicPartitionControlAndroid::set_fake_mapped_devices(
+ const std::set<std::string>& fake) {
+ mapped_devices_ = fake;
+}
+
+bool DynamicPartitionControlAndroid::IsRecovery() {
+ return constants::kIsRecovery;
+}
+
+static bool IsIncrementalUpdate(const DeltaArchiveManifest& manifest) {
+ const auto& partitions = manifest.partitions();
+ return std::any_of(partitions.begin(), partitions.end(), [](const auto& p) {
+ return p.has_old_partition_info();
+ });
+}
+
+bool DynamicPartitionControlAndroid::DeleteSourcePartitions(
+ MetadataBuilder* builder,
+ uint32_t source_slot,
+ const DeltaArchiveManifest& manifest) {
+ TEST_AND_RETURN_FALSE(IsRecovery());
+
+ if (IsIncrementalUpdate(manifest)) {
+ LOG(ERROR) << "Cannot sideload incremental OTA because snapshots cannot "
+ << "be created.";
+ if (GetVirtualAbFeatureFlag().IsLaunch()) {
+ LOG(ERROR) << "Sideloading incremental updates on devices launches "
+ << " Virtual A/B is not supported.";
+ }
+ return false;
+ }
+
+ LOG(INFO) << "Will overwrite existing partitions. Slot "
+ << BootControlInterface::SlotName(source_slot)
+ << "may be unbootable until update finishes!";
+ const std::string source_suffix = SlotSuffixForSlotNumber(source_slot);
+ DeleteGroupsWithSuffix(builder, source_suffix);
+
+ return true;
+}
+
+std::unique_ptr<AbstractAction>
+DynamicPartitionControlAndroid::GetCleanupPreviousUpdateAction(
+ BootControlInterface* boot_control,
+ PrefsInterface* prefs,
+ CleanupPreviousUpdateActionDelegateInterface* delegate) {
+ if (!GetVirtualAbFeatureFlag().IsEnabled()) {
+ return std::make_unique<NoOpAction>();
+ }
+ return std::make_unique<CleanupPreviousUpdateAction>(
+ prefs, boot_control, snapshot_.get(), delegate);
+}
+
+bool DynamicPartitionControlAndroid::ResetUpdate(PrefsInterface* prefs) {
+ if (!GetVirtualAbFeatureFlag().IsEnabled()) {
+ return true;
+ }
+
+ LOG(INFO) << __func__ << " resetting update state and deleting snapshots.";
+ TEST_AND_RETURN_FALSE(prefs != nullptr);
+
+ // If the device has already booted into the target slot,
+ // ResetUpdateProgress may pass but CancelUpdate fails.
+ // This is expected. A scheduled CleanupPreviousUpdateAction should free
+ // space when it is done.
+ TEST_AND_RETURN_FALSE(DeltaPerformer::ResetUpdateProgress(
+ prefs, false /* quick */, false /* skip dynamic partitions metadata */));
+
+ if (ExpectMetadataMounted()) {
+ TEST_AND_RETURN_FALSE(snapshot_->CancelUpdate());
+ } else {
+ LOG(INFO) << "Skip cancelling update in ResetUpdate because /metadata is "
+ << "not mounted";
+ }
+
+ return true;
+}
+
+bool DynamicPartitionControlAndroid::ListDynamicPartitionsForSlot(
+ uint32_t current_slot, std::vector<std::string>* partitions) {
+ if (!GetDynamicPartitionsFeatureFlag().IsEnabled()) {
+ LOG(ERROR) << "Dynamic partition is not enabled";
+ return false;
+ }
+
+ std::string device_dir_str;
+ TEST_AND_RETURN_FALSE(GetDeviceDir(&device_dir_str));
+ base::FilePath device_dir(device_dir_str);
+ auto super_device =
+ device_dir.Append(GetSuperPartitionName(current_slot)).value();
+ auto builder = LoadMetadataBuilder(super_device, current_slot);
+ TEST_AND_RETURN_FALSE(builder != nullptr);
+
+ std::vector<std::string> result;
+ auto suffix = SlotSuffixForSlotNumber(current_slot);
+ for (const auto& group : builder->ListGroups()) {
+ for (const auto& partition : builder->ListPartitionsInGroup(group)) {
+ std::string_view partition_name = partition->name();
+ if (!android::base::ConsumeSuffix(&partition_name, suffix)) {
+ continue;
+ }
+ result.emplace_back(partition_name);
+ }
+ }
+ *partitions = std::move(result);
+ return true;
+}
+
+bool DynamicPartitionControlAndroid::VerifyExtentsForUntouchedPartitions(
+ uint32_t source_slot,
+ uint32_t target_slot,
+ const std::vector<std::string>& partitions) {
+ std::string device_dir_str;
+ TEST_AND_RETURN_FALSE(GetDeviceDir(&device_dir_str));
+ base::FilePath device_dir(device_dir_str);
+
+ auto source_super_device =
+ device_dir.Append(GetSuperPartitionName(source_slot)).value();
+ auto source_builder = LoadMetadataBuilder(source_super_device, source_slot);
+ TEST_AND_RETURN_FALSE(source_builder != nullptr);
+
+ auto target_super_device =
+ device_dir.Append(GetSuperPartitionName(target_slot)).value();
+ auto target_builder = LoadMetadataBuilder(target_super_device, target_slot);
+ TEST_AND_RETURN_FALSE(target_builder != nullptr);
+
+ return MetadataBuilder::VerifyExtentsAgainstSourceMetadata(
+ *source_builder, source_slot, *target_builder, target_slot, partitions);
+}
+
+bool DynamicPartitionControlAndroid::ExpectMetadataMounted() {
+ // No need to mount metadata for non-Virtual A/B devices.
+ if (!GetVirtualAbFeatureFlag().IsEnabled()) {
+ return false;
+ }
+ // Intentionally not checking |metadata_device_| in Android mode.
+ // /metadata should always be mounted in Android mode. If it isn't, let caller
+ // fails when calling into SnapshotManager.
+ if (!IsRecovery()) {
+ return true;
+ }
+ // In recovery mode, explicitly check |metadata_device_|.
+ return metadata_device_ != nullptr;
+}
+
+bool DynamicPartitionControlAndroid::EnsureMetadataMounted() {
+ // No need to mount metadata for non-Virtual A/B devices.
+ if (!GetVirtualAbFeatureFlag().IsEnabled()) {
+ return true;
+ }
+
+ if (metadata_device_ == nullptr) {
+ metadata_device_ = snapshot_->EnsureMetadataMounted();
+ }
+ return metadata_device_ != nullptr;
+}
+
+std::unique_ptr<android::snapshot::ISnapshotWriter>
+DynamicPartitionControlAndroid::OpenCowWriter(
+ const std::string& partition_name,
+ const std::optional<std::string>& source_path,
+ bool is_append) {
+ auto suffix = SlotSuffixForSlotNumber(target_slot_);
+
+ auto super_device = GetSuperDevice();
+ if (!super_device.has_value()) {
+ return nullptr;
+ }
+ CreateLogicalPartitionParams params = {
+ .block_device = super_device->value(),
+ .metadata_slot = target_slot_,
+ .partition_name = partition_name + suffix,
+ .force_writable = true,
+ };
+ // TODO(zhangkelvin) Open an APPEND mode CowWriter once there's an API to do
+ // it.
+ return snapshot_->OpenSnapshotWriter(params, std::move(source_path));
+}
+
+std::optional<base::FilePath> DynamicPartitionControlAndroid::GetSuperDevice() {
+ std::string device_dir_str;
+ if (!GetDeviceDir(&device_dir_str)) {
+ LOG(ERROR) << "Failed to get device dir!";
+ return {};
+ }
+ base::FilePath device_dir(device_dir_str);
+ auto super_device = device_dir.Append(GetSuperPartitionName(target_slot_));
+ return super_device;
+}
+
+bool DynamicPartitionControlAndroid::MapAllPartitions() {
+ return snapshot_->MapAllSnapshots();
+}
+
+} // namespace chromeos_update_engine
diff --git a/aosp/dynamic_partition_control_android.h b/aosp/dynamic_partition_control_android.h
new file mode 100644
index 0000000..a4064e9
--- /dev/null
+++ b/aosp/dynamic_partition_control_android.h
@@ -0,0 +1,309 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_AOSP_DYNAMIC_PARTITION_CONTROL_ANDROID_H_
+#define UPDATE_ENGINE_AOSP_DYNAMIC_PARTITION_CONTROL_ANDROID_H_
+
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <base/files/file_util.h>
+#include <libsnapshot/auto_device.h>
+#include <libsnapshot/snapshot.h>
+#include <libsnapshot/snapshot_writer.h>
+
+#include "update_engine/common/dynamic_partition_control_interface.h"
+
+namespace chromeos_update_engine {
+
+class DynamicPartitionControlAndroid : public DynamicPartitionControlInterface {
+ public:
+ DynamicPartitionControlAndroid();
+ ~DynamicPartitionControlAndroid();
+ FeatureFlag GetDynamicPartitionsFeatureFlag() override;
+ FeatureFlag GetVirtualAbFeatureFlag() override;
+ FeatureFlag GetVirtualAbCompressionFeatureFlag() override;
+ bool OptimizeOperation(const std::string& partition_name,
+ const InstallOperation& operation,
+ InstallOperation* optimized) override;
+ void Cleanup() override;
+
+ bool PreparePartitionsForUpdate(uint32_t source_slot,
+ uint32_t target_slot,
+ const DeltaArchiveManifest& manifest,
+ bool update,
+ uint64_t* required_size) override;
+ bool FinishUpdate(bool powerwash_required) override;
+ std::unique_ptr<AbstractAction> GetCleanupPreviousUpdateAction(
+ BootControlInterface* boot_control,
+ PrefsInterface* prefs,
+ CleanupPreviousUpdateActionDelegateInterface* delegate) override;
+
+ bool ResetUpdate(PrefsInterface* prefs) override;
+
+ bool ListDynamicPartitionsForSlot(
+ uint32_t current_slot, std::vector<std::string>* partitions) override;
+
+ bool VerifyExtentsForUntouchedPartitions(
+ uint32_t source_slot,
+ uint32_t target_slot,
+ const std::vector<std::string>& partitions) override;
+
+ bool GetDeviceDir(std::string* path) override;
+
+ // Return the device for partition |partition_name| at slot |slot|.
+ // |current_slot| should be set to the current active slot.
+ // Note: this function is only used by BootControl*::GetPartitionDevice.
+ // Other callers should prefer BootControl*::GetPartitionDevice over
+ // BootControl*::GetDynamicPartitionControl()->GetPartitionDevice().
+ bool GetPartitionDevice(const std::string& partition_name,
+ uint32_t slot,
+ uint32_t current_slot,
+ bool not_in_payload,
+ std::string* device,
+ bool* is_dynamic);
+
+ bool GetPartitionDevice(const std::string& partition_name,
+ uint32_t slot,
+ uint32_t current_slot,
+ std::string* device);
+
+ // Partition name is expected to be unsuffixed. e.g. system, vendor
+ // Return an interface to write to a snapshoted partition.
+ std::unique_ptr<android::snapshot::ISnapshotWriter> OpenCowWriter(
+ const std::string& unsuffixed_partition_name,
+ const std::optional<std::string>& source_path,
+ bool is_append) override;
+
+ bool UnmapAllPartitions() override;
+
+ protected:
+ // These functions are exposed for testing.
+
+ // Unmap logical partition on device mapper. This is the reverse operation
+ // of MapPartitionOnDeviceMapper.
+ // Returns true if unmapped successfully.
+ virtual bool UnmapPartitionOnDeviceMapper(
+ const std::string& target_partition_name);
+
+ // Retrieves metadata from |super_device| at slot |slot|.
+ virtual std::unique_ptr<android::fs_mgr::MetadataBuilder> LoadMetadataBuilder(
+ const std::string& super_device, uint32_t slot);
+
+ // Retrieves metadata from |super_device| at slot |source_slot|. And modifies
+ // the metadata so that during updates, the metadata can be written to
+ // |target_slot|. In particular, on retrofit devices, the returned metadata
+ // automatically includes block devices at |target_slot|.
+ virtual std::unique_ptr<android::fs_mgr::MetadataBuilder> LoadMetadataBuilder(
+ const std::string& super_device,
+ uint32_t source_slot,
+ uint32_t target_slot);
+
+ // Write metadata |builder| to |super_device| at slot |target_slot|.
+ virtual bool StoreMetadata(const std::string& super_device,
+ android::fs_mgr::MetadataBuilder* builder,
+ uint32_t target_slot);
+
+ // Map logical partition on device-mapper.
+ // |super_device| is the device path of the physical partition ("super").
+ // |target_partition_name| is the identifier used in metadata; for example,
+ // "vendor_a"
+ // |slot| is the selected slot to mount; for example, 0 for "_a".
+ // Returns true if mapped successfully; if so, |path| is set to the device
+ // path of the mapped logical partition.
+ virtual bool MapPartitionOnDeviceMapper(
+ const std::string& super_device,
+ const std::string& target_partition_name,
+ uint32_t slot,
+ bool force_writable,
+ std::string* path);
+
+ // Return true if a static partition exists at device path |path|.
+ virtual bool DeviceExists(const std::string& path);
+
+ // Returns the current state of the underlying device mapper device
+ // with given name.
+ // One of INVALID, SUSPENDED or ACTIVE.
+ virtual android::dm::DmDeviceState GetState(const std::string& name);
+
+ // Returns the path to the device mapper device node in '/dev' corresponding
+ // to 'name'. If the device does not exist, false is returned, and the path
+ // parameter is not set.
+ virtual bool GetDmDevicePathByName(const std::string& name,
+ std::string* path);
+
+ // Return the name of the super partition (which stores super partition
+ // metadata) for a given slot.
+ virtual std::string GetSuperPartitionName(uint32_t slot);
+
+ virtual void set_fake_mapped_devices(const std::set<std::string>& fake);
+
+ // Allow mock objects to override this to test recovery mode.
+ virtual bool IsRecovery();
+
+ // Determine path for system_other partition.
+ // |source_slot| should be current slot.
+ // |target_slot| should be "other" slot.
+ // |partition_name_suffix| should be "system" + suffix(|target_slot|).
+ // Return true and set |path| if successful.
+ // Set |path| to empty if no need to erase system_other.
+ // Set |should_unmap| to true if path needs to be unmapped later.
+ //
+ // Note: system_other cannot use GetPartitionDevice or
+ // GetDynamicPartitionDevice because:
+ // - super partition metadata may be loaded from the source slot
+ // - UPDATED flag needs to be check to skip erasing if partition is not
+ // created by flashing tools
+ // - Snapshots from previous update attempts should not be used.
+ virtual bool GetSystemOtherPath(uint32_t source_slot,
+ uint32_t target_slot,
+ const std::string& partition_name_suffix,
+ std::string* path,
+ bool* should_unmap);
+
+ // Returns true if any entry in the fstab file in |path| has AVB enabled,
+ // false if not enabled, and nullopt for any error.
+ virtual std::optional<bool> IsAvbEnabledInFstab(const std::string& path);
+
+ // Returns true if system_other has AVB enabled, false if not enabled, and
+ // nullopt for any error.
+ virtual std::optional<bool> IsAvbEnabledOnSystemOther();
+
+ // Erase system_other partition that may contain system_other.img.
+ // After the update, the content of system_other may be corrupted but with
+ // valid AVB footer. If the update is rolled back and factory data reset is
+ // triggered, system_b fails to be mapped with verity errors (see
+ // b/152444348). Erase the system_other so that mapping system_other is
+ // skipped.
+ virtual bool EraseSystemOtherAvbFooter(uint32_t source_slot,
+ uint32_t target_slot);
+
+ // Helper for PreparePartitionsForUpdate. Used for devices with dynamic
+ // partitions updating without snapshots.
+ // If |delete_source| is set, source partitions are deleted before resizing
+ // target partitions (using DeleteSourcePartitions).
+ virtual bool PrepareDynamicPartitionsForUpdate(
+ uint32_t source_slot,
+ uint32_t target_slot,
+ const DeltaArchiveManifest& manifest,
+ bool delete_source);
+
+ bool MapAllPartitions() override;
+
+ private:
+ friend class DynamicPartitionControlAndroidTest;
+ friend class SnapshotPartitionTestP;
+
+ std::optional<base::FilePath> GetSuperDevice();
+
+ bool MapPartitionInternal(const std::string& super_device,
+ const std::string& target_partition_name,
+ uint32_t slot,
+ bool force_writable,
+ std::string* path);
+
+ // Update |builder| according to |partition_metadata|.
+ // - In Android mode, this is only called when the device
+ // does not have Virtual A/B.
+ // - When sideloading, this maybe called as a fallback path if CoW cannot
+ // be created.
+ bool UpdatePartitionMetadata(android::fs_mgr::MetadataBuilder* builder,
+ uint32_t target_slot,
+ const DeltaArchiveManifest& manifest);
+
+ // Helper for PreparePartitionsForUpdate. Used for snapshotted partitions for
+ // Virtual A/B update.
+ bool PrepareSnapshotPartitionsForUpdate(uint32_t source_slot,
+ uint32_t target_slot,
+ const DeltaArchiveManifest& manifest,
+ uint64_t* required_size);
+
+ enum class DynamicPartitionDeviceStatus {
+ SUCCESS,
+ ERROR,
+ TRY_STATIC,
+ };
+
+ // Return SUCCESS and path in |device| if partition is dynamic.
+ // Return ERROR if any error.
+ // Return TRY_STATIC if caller should resolve the partition as a static
+ // partition instead.
+ DynamicPartitionDeviceStatus GetDynamicPartitionDevice(
+ const base::FilePath& device_dir,
+ const std::string& partition_name_suffix,
+ uint32_t slot,
+ uint32_t current_slot,
+ bool not_in_payload,
+ std::string* device);
+
+ // Return true if |partition_name_suffix| is a block device of
+ // super partition metadata slot |slot|.
+ bool IsSuperBlockDevice(const base::FilePath& device_dir,
+ uint32_t current_slot,
+ const std::string& partition_name_suffix);
+
+ // If sideloading a full OTA, delete source partitions from |builder|.
+ bool DeleteSourcePartitions(android::fs_mgr::MetadataBuilder* builder,
+ uint32_t source_slot,
+ const DeltaArchiveManifest& manifest);
+
+ // Returns true if metadata is expected to be mounted, false otherwise.
+ // Note that it returns false on non-Virtual A/B devices.
+ //
+ // Almost all functions of SnapshotManager depends on metadata being mounted.
+ // - In Android mode for Virtual A/B devices, assume it is mounted. If not,
+ // let caller fails when calling into SnapshotManager.
+ // - In recovery for Virtual A/B devices, it is possible that metadata is not
+ // formatted, hence it cannot be mounted. Caller should not call into
+ // SnapshotManager.
+ // - On non-Virtual A/B devices, updates do not depend on metadata partition.
+ // Caller should not call into SnapshotManager.
+ //
+ // This function does NOT mount metadata partition. Use EnsureMetadataMounted
+ // to mount metadata partition.
+ bool ExpectMetadataMounted();
+
+ // Ensure /metadata is mounted. Returns true if successful, false otherwise.
+ //
+ // Note that this function returns true on non-Virtual A/B devices without
+ // doing anything.
+ bool EnsureMetadataMounted();
+
+ // Set boolean flags related to target build. This includes flags like
+ // target_supports_snapshot_ and is_target_dynamic_.
+ bool SetTargetBuildVars(const DeltaArchiveManifest& manifest);
+
+ std::set<std::string> mapped_devices_;
+ const FeatureFlag dynamic_partitions_;
+ const FeatureFlag virtual_ab_;
+ const FeatureFlag virtual_ab_compression_;
+ std::unique_ptr<android::snapshot::ISnapshotManager> snapshot_;
+ std::unique_ptr<android::snapshot::AutoDevice> metadata_device_;
+ bool target_supports_snapshot_ = false;
+ // Whether the target partitions should be loaded as dynamic partitions. Set
+ // by PreparePartitionsForUpdate() per each update.
+ bool is_target_dynamic_ = false;
+ uint32_t source_slot_ = UINT32_MAX;
+ uint32_t target_slot_ = UINT32_MAX;
+
+ DISALLOW_COPY_AND_ASSIGN(DynamicPartitionControlAndroid);
+};
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_AOSP_DYNAMIC_PARTITION_CONTROL_ANDROID_H_
diff --git a/aosp/dynamic_partition_control_android_unittest.cc b/aosp/dynamic_partition_control_android_unittest.cc
new file mode 100644
index 0000000..5d6463b
--- /dev/null
+++ b/aosp/dynamic_partition_control_android_unittest.cc
@@ -0,0 +1,1028 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/aosp/dynamic_partition_control_android.h"
+
+#include <set>
+#include <vector>
+
+#include <base/logging.h>
+#include <base/strings/string_util.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <libavb/libavb.h>
+#include <libsnapshot/mock_snapshot.h>
+
+#include "update_engine/aosp/dynamic_partition_test_utils.h"
+#include "update_engine/aosp/mock_dynamic_partition_control.h"
+#include "update_engine/common/mock_prefs.h"
+#include "update_engine/common/test_utils.h"
+
+using android::dm::DmDeviceState;
+using android::snapshot::MockSnapshotManager;
+using chromeos_update_engine::test_utils::ScopedLoopbackDeviceBinder;
+using std::string;
+using testing::_;
+using testing::AnyNumber;
+using testing::AnyOf;
+using testing::Invoke;
+using testing::NiceMock;
+using testing::Not;
+using testing::Optional;
+using testing::Return;
+
+namespace chromeos_update_engine {
+
+class DynamicPartitionControlAndroidTest : public ::testing::Test {
+ public:
+ void SetUp() override {
+ module_ = std::make_unique<NiceMock<MockDynamicPartitionControlAndroid>>();
+
+ ON_CALL(dynamicControl(), GetDynamicPartitionsFeatureFlag())
+ .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::LAUNCH)));
+ ON_CALL(dynamicControl(), GetVirtualAbFeatureFlag())
+ .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::NONE)));
+
+ ON_CALL(dynamicControl(), GetDeviceDir(_))
+ .WillByDefault(Invoke([](auto path) {
+ *path = kFakeDevicePath;
+ return true;
+ }));
+
+ ON_CALL(dynamicControl(), GetSuperPartitionName(_))
+ .WillByDefault(Return(kFakeSuper));
+
+ ON_CALL(dynamicControl(), GetDmDevicePathByName(_, _))
+ .WillByDefault(Invoke([](auto partition_name_suffix, auto device) {
+ *device = GetDmDevice(partition_name_suffix);
+ return true;
+ }));
+
+ ON_CALL(dynamicControl(), EraseSystemOtherAvbFooter(_, _))
+ .WillByDefault(Return(true));
+
+ ON_CALL(dynamicControl(), IsRecovery()).WillByDefault(Return(false));
+
+ ON_CALL(dynamicControl(), PrepareDynamicPartitionsForUpdate(_, _, _, _))
+ .WillByDefault(Invoke([&](uint32_t source_slot,
+ uint32_t target_slot,
+ const DeltaArchiveManifest& manifest,
+ bool delete_source) {
+ return dynamicControl().RealPrepareDynamicPartitionsForUpdate(
+ source_slot, target_slot, manifest, delete_source);
+ }));
+ }
+
+ // Return the mocked DynamicPartitionControlInterface.
+ NiceMock<MockDynamicPartitionControlAndroid>& dynamicControl() {
+ return static_cast<NiceMock<MockDynamicPartitionControlAndroid>&>(*module_);
+ }
+
+ std::string GetSuperDevice(uint32_t slot) {
+ return GetDevice(dynamicControl().GetSuperPartitionName(slot));
+ }
+
+ uint32_t source() { return slots_.source; }
+ uint32_t target() { return slots_.target; }
+
+ // Return partition names with suffix of source().
+ std::string S(const std::string& name) {
+ return name + kSlotSuffixes[source()];
+ }
+
+ // Return partition names with suffix of target().
+ std::string T(const std::string& name) {
+ return name + kSlotSuffixes[target()];
+ }
+
+ // Set the fake metadata to return when LoadMetadataBuilder is called on
+ // |slot|.
+ void SetMetadata(uint32_t slot,
+ const PartitionSuffixSizes& sizes,
+ uint32_t partition_attr = 0,
+ uint64_t super_size = kDefaultSuperSize) {
+ EXPECT_CALL(dynamicControl(),
+ LoadMetadataBuilder(GetSuperDevice(slot), slot))
+ .Times(AnyNumber())
+ .WillRepeatedly(Invoke([=](auto, auto) {
+ return NewFakeMetadata(PartitionSuffixSizesToManifest(sizes),
+ partition_attr,
+ super_size);
+ }));
+
+ EXPECT_CALL(dynamicControl(),
+ LoadMetadataBuilder(GetSuperDevice(slot), slot, _))
+ .Times(AnyNumber())
+ .WillRepeatedly(Invoke([=](auto, auto, auto) {
+ return NewFakeMetadata(PartitionSuffixSizesToManifest(sizes),
+ partition_attr,
+ super_size);
+ }));
+ }
+
+ void ExpectStoreMetadata(const PartitionSuffixSizes& partition_sizes) {
+ EXPECT_CALL(dynamicControl(),
+ StoreMetadata(GetSuperDevice(target()),
+ MetadataMatches(partition_sizes),
+ target()))
+ .WillOnce(Return(true));
+ }
+
+ // Expect that UnmapPartitionOnDeviceMapper is called on target() metadata
+ // slot with each partition in |partitions|.
+ void ExpectUnmap(const std::set<std::string>& partitions) {
+ // Error when UnmapPartitionOnDeviceMapper is called on unknown arguments.
+ ON_CALL(dynamicControl(), UnmapPartitionOnDeviceMapper(_))
+ .WillByDefault(Return(false));
+
+ for (const auto& partition : partitions) {
+ EXPECT_CALL(dynamicControl(), UnmapPartitionOnDeviceMapper(partition))
+ .WillOnce(Return(true));
+ }
+ }
+ bool PreparePartitionsForUpdate(const PartitionSizes& partition_sizes) {
+ return dynamicControl().PreparePartitionsForUpdate(
+ source(),
+ target(),
+ PartitionSizesToManifest(partition_sizes),
+ true,
+ nullptr);
+ }
+ void SetSlots(const TestParam& slots) { slots_ = slots; }
+
+ void SetSnapshotEnabled(bool enabled) {
+ dynamicControl().target_supports_snapshot_ = enabled;
+ }
+
+ struct Listener : public ::testing::MatchResultListener {
+ explicit Listener(std::ostream* os) : MatchResultListener(os) {}
+ };
+
+ testing::AssertionResult UpdatePartitionMetadata(
+ const PartitionSuffixSizes& source_metadata,
+ const PartitionSizes& update_metadata,
+ const PartitionSuffixSizes& expected) {
+ return UpdatePartitionMetadata(
+ PartitionSuffixSizesToManifest(source_metadata),
+ PartitionSizesToManifest(update_metadata),
+ PartitionSuffixSizesToManifest(expected));
+ }
+ testing::AssertionResult UpdatePartitionMetadata(
+ const DeltaArchiveManifest& source_manifest,
+ const DeltaArchiveManifest& update_manifest,
+ const DeltaArchiveManifest& expected) {
+ return UpdatePartitionMetadata(
+ source_manifest, update_manifest, MetadataMatches(expected));
+ }
+ testing::AssertionResult UpdatePartitionMetadata(
+ const DeltaArchiveManifest& source_manifest,
+ const DeltaArchiveManifest& update_manifest,
+ const Matcher<MetadataBuilder*>& matcher) {
+ auto super_metadata = NewFakeMetadata(source_manifest);
+ if (!module_->UpdatePartitionMetadata(
+ super_metadata.get(), target(), update_manifest)) {
+ return testing::AssertionFailure()
+ << "UpdatePartitionMetadataInternal failed";
+ }
+ std::stringstream ss;
+ Listener listener(&ss);
+ if (matcher.MatchAndExplain(super_metadata.get(), &listener)) {
+ return testing::AssertionSuccess() << ss.str();
+ } else {
+ return testing::AssertionFailure() << ss.str();
+ }
+ }
+
+ std::unique_ptr<DynamicPartitionControlAndroid> module_;
+ TestParam slots_;
+};
+
+class DynamicPartitionControlAndroidTestP
+ : public DynamicPartitionControlAndroidTest,
+ public ::testing::WithParamInterface<TestParam> {
+ public:
+ void SetUp() override {
+ DynamicPartitionControlAndroidTest::SetUp();
+ SetSlots(GetParam());
+ }
+};
+
+// Test resize case. Grow if target metadata contains a partition with a size
+// less than expected.
+TEST_P(DynamicPartitionControlAndroidTestP,
+ NeedGrowIfSizeNotMatchWhenResizing) {
+ PartitionSuffixSizes source_metadata{{S("system"), 2_GiB},
+ {S("vendor"), 1_GiB},
+ {T("system"), 2_GiB},
+ {T("vendor"), 1_GiB}};
+ PartitionSuffixSizes expected{{S("system"), 2_GiB},
+ {S("vendor"), 1_GiB},
+ {T("system"), 3_GiB},
+ {T("vendor"), 1_GiB}};
+ PartitionSizes update_metadata{{"system", 3_GiB}, {"vendor", 1_GiB}};
+ EXPECT_TRUE(
+ UpdatePartitionMetadata(source_metadata, update_metadata, expected));
+}
+
+// Test resize case. Shrink if target metadata contains a partition with a size
+// greater than expected.
+TEST_P(DynamicPartitionControlAndroidTestP,
+ NeedShrinkIfSizeNotMatchWhenResizing) {
+ PartitionSuffixSizes source_metadata{{S("system"), 2_GiB},
+ {S("vendor"), 1_GiB},
+ {T("system"), 2_GiB},
+ {T("vendor"), 1_GiB}};
+ PartitionSuffixSizes expected{{S("system"), 2_GiB},
+ {S("vendor"), 1_GiB},
+ {T("system"), 2_GiB},
+ {T("vendor"), 150_MiB}};
+ PartitionSizes update_metadata{{"system", 2_GiB}, {"vendor", 150_MiB}};
+ EXPECT_TRUE(
+ UpdatePartitionMetadata(source_metadata, update_metadata, expected));
+}
+
+// Test adding partitions on the first run.
+TEST_P(DynamicPartitionControlAndroidTestP, AddPartitionToEmptyMetadata) {
+ PartitionSuffixSizes source_metadata{};
+ PartitionSuffixSizes expected{{T("system"), 2_GiB}, {T("vendor"), 1_GiB}};
+ PartitionSizes update_metadata{{"system", 2_GiB}, {"vendor", 1_GiB}};
+ EXPECT_TRUE(
+ UpdatePartitionMetadata(source_metadata, update_metadata, expected));
+}
+
+// Test subsequent add case.
+TEST_P(DynamicPartitionControlAndroidTestP, AddAdditionalPartition) {
+ PartitionSuffixSizes source_metadata{{S("system"), 2_GiB},
+ {T("system"), 2_GiB}};
+ PartitionSuffixSizes expected{
+ {S("system"), 2_GiB}, {T("system"), 2_GiB}, {T("vendor"), 1_GiB}};
+ PartitionSizes update_metadata{{"system", 2_GiB}, {"vendor", 1_GiB}};
+ EXPECT_TRUE(
+ UpdatePartitionMetadata(source_metadata, update_metadata, expected));
+}
+
+// Test delete one partition.
+TEST_P(DynamicPartitionControlAndroidTestP, DeletePartition) {
+ PartitionSuffixSizes source_metadata{{S("system"), 2_GiB},
+ {S("vendor"), 1_GiB},
+ {T("system"), 2_GiB},
+ {T("vendor"), 1_GiB}};
+ // No T("vendor")
+ PartitionSuffixSizes expected{
+ {S("system"), 2_GiB}, {S("vendor"), 1_GiB}, {T("system"), 2_GiB}};
+ PartitionSizes update_metadata{{"system", 2_GiB}};
+ EXPECT_TRUE(
+ UpdatePartitionMetadata(source_metadata, update_metadata, expected));
+}
+
+// Test delete all partitions.
+TEST_P(DynamicPartitionControlAndroidTestP, DeleteAll) {
+ PartitionSuffixSizes source_metadata{{S("system"), 2_GiB},
+ {S("vendor"), 1_GiB},
+ {T("system"), 2_GiB},
+ {T("vendor"), 1_GiB}};
+ PartitionSuffixSizes expected{{S("system"), 2_GiB}, {S("vendor"), 1_GiB}};
+ PartitionSizes update_metadata{};
+ EXPECT_TRUE(
+ UpdatePartitionMetadata(source_metadata, update_metadata, expected));
+}
+
+// Test corrupt source metadata case.
+TEST_P(DynamicPartitionControlAndroidTestP, CorruptedSourceMetadata) {
+ EXPECT_CALL(dynamicControl(),
+ LoadMetadataBuilder(GetSuperDevice(source()), source(), _))
+ .WillOnce(Invoke([](auto, auto, auto) { return nullptr; }));
+ ExpectUnmap({T("system")});
+
+ EXPECT_FALSE(PreparePartitionsForUpdate({{"system", 1_GiB}}))
+ << "Should not be able to continue with corrupt source metadata";
+}
+
+// Test that UpdatePartitionMetadata fails if there is not enough space on the
+// device.
+TEST_P(DynamicPartitionControlAndroidTestP, NotEnoughSpace) {
+ PartitionSuffixSizes source_metadata{{S("system"), 3_GiB},
+ {S("vendor"), 2_GiB},
+ {T("system"), 0},
+ {T("vendor"), 0}};
+ PartitionSizes update_metadata{{"system", 3_GiB}, {"vendor", 3_GiB}};
+
+ EXPECT_FALSE(UpdatePartitionMetadata(source_metadata, update_metadata, {}))
+ << "Should not be able to fit 11GiB data into 10GiB space";
+}
+
+TEST_P(DynamicPartitionControlAndroidTestP, NotEnoughSpaceForSlot) {
+ PartitionSuffixSizes source_metadata{{S("system"), 1_GiB},
+ {S("vendor"), 1_GiB},
+ {T("system"), 0},
+ {T("vendor"), 0}};
+ PartitionSizes update_metadata{{"system", 3_GiB}, {"vendor", 3_GiB}};
+ EXPECT_FALSE(UpdatePartitionMetadata(source_metadata, update_metadata, {}))
+ << "Should not be able to grow over size of super / 2";
+}
+
+TEST_P(DynamicPartitionControlAndroidTestP,
+ ApplyRetrofitUpdateOnDynamicPartitionsEnabledBuild) {
+ ON_CALL(dynamicControl(), GetDynamicPartitionsFeatureFlag())
+ .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::RETROFIT)));
+ // Static partition {system,bar}_{a,b} exists.
+ EXPECT_CALL(dynamicControl(),
+ DeviceExists(AnyOf(GetDevice(S("bar")),
+ GetDevice(T("bar")),
+ GetDevice(S("system")),
+ GetDevice(T("system")))))
+ .WillRepeatedly(Return(true));
+
+ SetMetadata(source(),
+ {{S("system"), 2_GiB},
+ {S("vendor"), 1_GiB},
+ {T("system"), 2_GiB},
+ {T("vendor"), 1_GiB}});
+
+ // Not calling through
+ // DynamicPartitionControlAndroidTest::PreparePartitionsForUpdate(), since we
+ // don't want any default group in the PartitionMetadata.
+ EXPECT_TRUE(dynamicControl().PreparePartitionsForUpdate(
+ source(), target(), {}, true, nullptr));
+
+ // Should use dynamic source partitions.
+ EXPECT_CALL(dynamicControl(), GetState(S("system")))
+ .Times(1)
+ .WillOnce(Return(DmDeviceState::ACTIVE));
+ string system_device;
+ EXPECT_TRUE(dynamicControl().GetPartitionDevice(
+ "system", source(), source(), &system_device));
+ EXPECT_EQ(GetDmDevice(S("system")), system_device);
+
+ // Should use static target partitions without querying dynamic control.
+ EXPECT_CALL(dynamicControl(), GetState(T("system"))).Times(0);
+ EXPECT_TRUE(dynamicControl().GetPartitionDevice(
+ "system", target(), source(), &system_device));
+ EXPECT_EQ(GetDevice(T("system")), system_device);
+
+ // Static partition "bar".
+ EXPECT_CALL(dynamicControl(), GetState(S("bar"))).Times(0);
+ std::string bar_device;
+ EXPECT_TRUE(dynamicControl().GetPartitionDevice(
+ "bar", source(), source(), &bar_device));
+ EXPECT_EQ(GetDevice(S("bar")), bar_device);
+
+ EXPECT_CALL(dynamicControl(), GetState(T("bar"))).Times(0);
+ EXPECT_TRUE(dynamicControl().GetPartitionDevice(
+ "bar", target(), source(), &bar_device));
+ EXPECT_EQ(GetDevice(T("bar")), bar_device);
+}
+
+TEST_P(DynamicPartitionControlAndroidTestP,
+ GetPartitionDeviceWhenResumingUpdate) {
+ // Static partition bar_{a,b} exists.
+ EXPECT_CALL(dynamicControl(),
+ DeviceExists(AnyOf(GetDevice(S("bar")), GetDevice(T("bar")))))
+ .WillRepeatedly(Return(true));
+
+ // Both of the two slots contain valid partition metadata, since this is
+ // resuming an update.
+ SetMetadata(source(),
+ {{S("system"), 2_GiB},
+ {S("vendor"), 1_GiB},
+ {T("system"), 2_GiB},
+ {T("vendor"), 1_GiB}});
+ SetMetadata(target(),
+ {{S("system"), 2_GiB},
+ {S("vendor"), 1_GiB},
+ {T("system"), 2_GiB},
+ {T("vendor"), 1_GiB}});
+
+ EXPECT_TRUE(dynamicControl().PreparePartitionsForUpdate(
+ source(),
+ target(),
+ PartitionSizesToManifest({{"system", 2_GiB}, {"vendor", 1_GiB}}),
+ false,
+ nullptr));
+
+ // Dynamic partition "system".
+ EXPECT_CALL(dynamicControl(), GetState(S("system")))
+ .Times(1)
+ .WillOnce(Return(DmDeviceState::ACTIVE));
+ string system_device;
+ EXPECT_TRUE(dynamicControl().GetPartitionDevice(
+ "system", source(), source(), &system_device));
+ EXPECT_EQ(GetDmDevice(S("system")), system_device);
+
+ EXPECT_CALL(dynamicControl(), GetState(T("system")))
+ .Times(AnyNumber())
+ .WillOnce(Return(DmDeviceState::ACTIVE));
+ EXPECT_CALL(dynamicControl(),
+ MapPartitionOnDeviceMapper(
+ GetSuperDevice(target()), T("system"), target(), _, _))
+ .Times(AnyNumber())
+ .WillRepeatedly(
+ Invoke([](const auto&, const auto& name, auto, auto, auto* device) {
+ *device = "/fake/remapped/" + name;
+ return true;
+ }));
+ EXPECT_TRUE(dynamicControl().GetPartitionDevice(
+ "system", target(), source(), &system_device));
+ EXPECT_EQ("/fake/remapped/" + T("system"), system_device);
+
+ // Static partition "bar".
+ EXPECT_CALL(dynamicControl(), GetState(S("bar"))).Times(0);
+ std::string bar_device;
+ EXPECT_TRUE(dynamicControl().GetPartitionDevice(
+ "bar", source(), source(), &bar_device));
+ EXPECT_EQ(GetDevice(S("bar")), bar_device);
+
+ EXPECT_CALL(dynamicControl(), GetState(T("bar"))).Times(0);
+ EXPECT_TRUE(dynamicControl().GetPartitionDevice(
+ "bar", target(), source(), &bar_device));
+ EXPECT_EQ(GetDevice(T("bar")), bar_device);
+}
+
+INSTANTIATE_TEST_CASE_P(DynamicPartitionControlAndroidTest,
+ DynamicPartitionControlAndroidTestP,
+ testing::Values(TestParam{0, 1}, TestParam{1, 0}));
+
+class DynamicPartitionControlAndroidGroupTestP
+ : public DynamicPartitionControlAndroidTestP {
+ public:
+ DeltaArchiveManifest source_manifest;
+ void SetUp() override {
+ DynamicPartitionControlAndroidTestP::SetUp();
+ AddGroupAndPartition(
+ &source_manifest, S("android"), 3_GiB, S("system"), 2_GiB);
+ AddGroupAndPartition(&source_manifest, S("oem"), 2_GiB, S("vendor"), 1_GiB);
+ AddGroupAndPartition(&source_manifest, T("android"), 3_GiB, T("system"), 0);
+ AddGroupAndPartition(&source_manifest, T("oem"), 2_GiB, T("vendor"), 0);
+ }
+
+ void AddGroupAndPartition(DeltaArchiveManifest* manifest,
+ const string& group,
+ uint64_t group_size,
+ const string& partition,
+ uint64_t partition_size) {
+ auto* g = AddGroup(manifest, group, group_size);
+ AddPartition(manifest, g, partition, partition_size);
+ }
+};
+
+// Allow to resize within group.
+TEST_P(DynamicPartitionControlAndroidGroupTestP, ResizeWithinGroup) {
+ DeltaArchiveManifest expected;
+ AddGroupAndPartition(&expected, T("android"), 3_GiB, T("system"), 3_GiB);
+ AddGroupAndPartition(&expected, T("oem"), 2_GiB, T("vendor"), 2_GiB);
+
+ DeltaArchiveManifest update_manifest;
+ AddGroupAndPartition(&update_manifest, "android", 3_GiB, "system", 3_GiB);
+ AddGroupAndPartition(&update_manifest, "oem", 2_GiB, "vendor", 2_GiB);
+
+ EXPECT_TRUE(
+ UpdatePartitionMetadata(source_manifest, update_manifest, expected));
+}
+
+TEST_P(DynamicPartitionControlAndroidGroupTestP, NotEnoughSpaceForGroup) {
+ DeltaArchiveManifest update_manifest;
+ AddGroupAndPartition(&update_manifest, "android", 3_GiB, "system", 1_GiB),
+ AddGroupAndPartition(&update_manifest, "oem", 2_GiB, "vendor", 3_GiB);
+ EXPECT_FALSE(UpdatePartitionMetadata(source_manifest, update_manifest, {}))
+ << "Should not be able to grow over maximum size of group";
+}
+
+TEST_P(DynamicPartitionControlAndroidGroupTestP, GroupTooBig) {
+ DeltaArchiveManifest update_manifest;
+ AddGroup(&update_manifest, "android", 3_GiB);
+ AddGroup(&update_manifest, "oem", 3_GiB);
+ EXPECT_FALSE(UpdatePartitionMetadata(source_manifest, update_manifest, {}))
+ << "Should not be able to grow over size of super / 2";
+}
+
+TEST_P(DynamicPartitionControlAndroidGroupTestP, AddPartitionToGroup) {
+ DeltaArchiveManifest expected;
+ auto* g = AddGroup(&expected, T("android"), 3_GiB);
+ AddPartition(&expected, g, T("system"), 2_GiB);
+ AddPartition(&expected, g, T("system_ext"), 1_GiB);
+
+ DeltaArchiveManifest update_manifest;
+ g = AddGroup(&update_manifest, "android", 3_GiB);
+ AddPartition(&update_manifest, g, "system", 2_GiB);
+ AddPartition(&update_manifest, g, "system_ext", 1_GiB);
+ AddGroupAndPartition(&update_manifest, "oem", 2_GiB, "vendor", 2_GiB);
+
+ EXPECT_TRUE(
+ UpdatePartitionMetadata(source_manifest, update_manifest, expected));
+}
+
+TEST_P(DynamicPartitionControlAndroidGroupTestP, RemovePartitionFromGroup) {
+ DeltaArchiveManifest expected;
+ AddGroup(&expected, T("android"), 3_GiB);
+
+ DeltaArchiveManifest update_manifest;
+ AddGroup(&update_manifest, "android", 3_GiB);
+ AddGroupAndPartition(&update_manifest, "oem", 2_GiB, "vendor", 2_GiB);
+
+ EXPECT_TRUE(
+ UpdatePartitionMetadata(source_manifest, update_manifest, expected));
+}
+
+TEST_P(DynamicPartitionControlAndroidGroupTestP, AddGroup) {
+ DeltaArchiveManifest expected;
+ AddGroupAndPartition(
+ &expected, T("new_group"), 2_GiB, T("new_partition"), 2_GiB);
+
+ DeltaArchiveManifest update_manifest;
+ AddGroupAndPartition(&update_manifest, "android", 2_GiB, "system", 2_GiB);
+ AddGroupAndPartition(&update_manifest, "oem", 1_GiB, "vendor", 1_GiB);
+ AddGroupAndPartition(
+ &update_manifest, "new_group", 2_GiB, "new_partition", 2_GiB);
+ EXPECT_TRUE(
+ UpdatePartitionMetadata(source_manifest, update_manifest, expected));
+}
+
+TEST_P(DynamicPartitionControlAndroidGroupTestP, RemoveGroup) {
+ DeltaArchiveManifest update_manifest;
+ AddGroupAndPartition(&update_manifest, "android", 2_GiB, "system", 2_GiB);
+
+ EXPECT_TRUE(UpdatePartitionMetadata(
+ source_manifest, update_manifest, Not(HasGroup(T("oem")))));
+}
+
+TEST_P(DynamicPartitionControlAndroidGroupTestP, ResizeGroup) {
+ DeltaArchiveManifest expected;
+ AddGroupAndPartition(&expected, T("android"), 2_GiB, T("system"), 2_GiB);
+ AddGroupAndPartition(&expected, T("oem"), 3_GiB, T("vendor"), 3_GiB);
+ DeltaArchiveManifest update_manifest;
+ AddGroupAndPartition(&update_manifest, "android", 2_GiB, "system", 2_GiB),
+ AddGroupAndPartition(&update_manifest, "oem", 3_GiB, "vendor", 3_GiB);
+ EXPECT_TRUE(
+ UpdatePartitionMetadata(source_manifest, update_manifest, expected));
+}
+
+INSTANTIATE_TEST_CASE_P(DynamicPartitionControlAndroidTest,
+ DynamicPartitionControlAndroidGroupTestP,
+ testing::Values(TestParam{0, 1}, TestParam{1, 0}));
+
+const PartitionSuffixSizes update_sizes_0() {
+ // Initial state is 0 for "other" slot.
+ return {
+ {"grown_a", 2_GiB},
+ {"shrunk_a", 1_GiB},
+ {"same_a", 100_MiB},
+ {"deleted_a", 150_MiB},
+ // no added_a
+ {"grown_b", 200_MiB},
+ // simulate system_other
+ {"shrunk_b", 0},
+ {"same_b", 0},
+ {"deleted_b", 0},
+ // no added_b
+ };
+}
+
+const PartitionSuffixSizes update_sizes_1() {
+ return {
+ {"grown_a", 2_GiB},
+ {"shrunk_a", 1_GiB},
+ {"same_a", 100_MiB},
+ {"deleted_a", 150_MiB},
+ // no added_a
+ {"grown_b", 3_GiB},
+ {"shrunk_b", 150_MiB},
+ {"same_b", 100_MiB},
+ {"added_b", 150_MiB},
+ // no deleted_b
+ };
+}
+
+const PartitionSuffixSizes update_sizes_2() {
+ return {
+ {"grown_a", 4_GiB},
+ {"shrunk_a", 100_MiB},
+ {"same_a", 100_MiB},
+ {"deleted_a", 64_MiB},
+ // no added_a
+ {"grown_b", 3_GiB},
+ {"shrunk_b", 150_MiB},
+ {"same_b", 100_MiB},
+ {"added_b", 150_MiB},
+ // no deleted_b
+ };
+}
+
+// Test case for first update after the device is manufactured, in which
+// case the "other" slot is likely of size "0" (except system, which is
+// non-zero because of system_other partition)
+TEST_F(DynamicPartitionControlAndroidTest, SimulatedFirstUpdate) {
+ SetSlots({0, 1});
+
+ SetMetadata(source(), update_sizes_0());
+ SetMetadata(target(), update_sizes_0());
+ ExpectStoreMetadata(update_sizes_1());
+ ExpectUnmap({"grown_b", "shrunk_b", "same_b", "added_b"});
+
+ EXPECT_TRUE(PreparePartitionsForUpdate({{"grown", 3_GiB},
+ {"shrunk", 150_MiB},
+ {"same", 100_MiB},
+ {"added", 150_MiB}}));
+}
+
+// After first update, test for the second update. In the second update, the
+// "added" partition is deleted and "deleted" partition is re-added.
+TEST_F(DynamicPartitionControlAndroidTest, SimulatedSecondUpdate) {
+ SetSlots({1, 0});
+
+ SetMetadata(source(), update_sizes_1());
+ SetMetadata(target(), update_sizes_0());
+
+ ExpectStoreMetadata(update_sizes_2());
+ ExpectUnmap({"grown_a", "shrunk_a", "same_a", "deleted_a"});
+
+ EXPECT_TRUE(PreparePartitionsForUpdate({{"grown", 4_GiB},
+ {"shrunk", 100_MiB},
+ {"same", 100_MiB},
+ {"deleted", 64_MiB}}));
+}
+
+TEST_F(DynamicPartitionControlAndroidTest, ApplyingToCurrentSlot) {
+ SetSlots({1, 1});
+ EXPECT_FALSE(PreparePartitionsForUpdate({}))
+ << "Should not be able to apply to current slot.";
+}
+
+TEST_P(DynamicPartitionControlAndroidTestP, OptimizeOperationTest) {
+ ASSERT_TRUE(dynamicControl().PreparePartitionsForUpdate(
+ source(),
+ target(),
+ PartitionSizesToManifest({{"foo", 4_MiB}}),
+ false,
+ nullptr));
+ dynamicControl().set_fake_mapped_devices({T("foo")});
+
+ InstallOperation iop;
+ InstallOperation optimized;
+ Extent *se, *de;
+
+ // Not a SOURCE_COPY operation, cannot skip.
+ iop.set_type(InstallOperation::REPLACE);
+ EXPECT_FALSE(dynamicControl().OptimizeOperation("foo", iop, &optimized));
+
+ iop.set_type(InstallOperation::SOURCE_COPY);
+
+ // By default GetVirtualAbFeatureFlag is disabled. Cannot skip operation.
+ EXPECT_FALSE(dynamicControl().OptimizeOperation("foo", iop, &optimized));
+
+ // Enable GetVirtualAbFeatureFlag in the mock interface.
+ ON_CALL(dynamicControl(), GetVirtualAbFeatureFlag())
+ .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::LAUNCH)));
+
+ // By default target_supports_snapshot_ is set to false. Cannot skip
+ // operation.
+ EXPECT_FALSE(dynamicControl().OptimizeOperation("foo", iop, &optimized));
+
+ SetSnapshotEnabled(true);
+
+ // Empty source and destination. Skip.
+ EXPECT_TRUE(dynamicControl().OptimizeOperation("foo", iop, &optimized));
+ EXPECT_TRUE(optimized.src_extents().empty());
+ EXPECT_TRUE(optimized.dst_extents().empty());
+
+ se = iop.add_src_extents();
+ se->set_start_block(0);
+ se->set_num_blocks(1);
+
+ // There is something in sources, but destinations are empty. Cannot skip.
+ EXPECT_FALSE(dynamicControl().OptimizeOperation("foo", iop, &optimized));
+
+ InstallOperation iop2;
+
+ de = iop2.add_dst_extents();
+ de->set_start_block(0);
+ de->set_num_blocks(1);
+
+ // There is something in destinations, but sources are empty. Cannot skip.
+ EXPECT_FALSE(dynamicControl().OptimizeOperation("foo", iop2, &optimized));
+
+ de = iop.add_dst_extents();
+ de->set_start_block(0);
+ de->set_num_blocks(1);
+
+ // Sources and destinations are identical. Skip.
+ EXPECT_TRUE(dynamicControl().OptimizeOperation("foo", iop, &optimized));
+ EXPECT_TRUE(optimized.src_extents().empty());
+ EXPECT_TRUE(optimized.dst_extents().empty());
+
+ se = iop.add_src_extents();
+ se->set_start_block(1);
+ se->set_num_blocks(5);
+
+ // There is something in source, but not in destination. Cannot skip.
+ EXPECT_FALSE(dynamicControl().OptimizeOperation("foo", iop, &optimized));
+
+ de = iop.add_dst_extents();
+ de->set_start_block(1);
+ de->set_num_blocks(5);
+
+ // There is source and destination are equal. Skip.
+ EXPECT_TRUE(dynamicControl().OptimizeOperation("foo", iop, &optimized));
+ EXPECT_TRUE(optimized.src_extents().empty());
+ EXPECT_TRUE(optimized.dst_extents().empty());
+
+ de = iop.add_dst_extents();
+ de->set_start_block(6);
+ de->set_num_blocks(5);
+
+ // There is something extra in dest. Cannot skip.
+ EXPECT_FALSE(dynamicControl().OptimizeOperation("foo", iop, &optimized));
+
+ se = iop.add_src_extents();
+ se->set_start_block(6);
+ se->set_num_blocks(5);
+
+ // Source and dest are identical again. Skip.
+ EXPECT_TRUE(dynamicControl().OptimizeOperation("foo", iop, &optimized));
+ EXPECT_TRUE(optimized.src_extents().empty());
+ EXPECT_TRUE(optimized.dst_extents().empty());
+
+ iop.Clear();
+ iop.set_type(InstallOperation::SOURCE_COPY);
+ se = iop.add_src_extents();
+ se->set_start_block(1);
+ se->set_num_blocks(1);
+ se = iop.add_src_extents();
+ se->set_start_block(3);
+ se->set_num_blocks(2);
+ se = iop.add_src_extents();
+ se->set_start_block(7);
+ se->set_num_blocks(2);
+ de = iop.add_dst_extents();
+ de->set_start_block(2);
+ de->set_num_blocks(5);
+
+ // [1, 3, 4, 7, 8] -> [2, 3, 4, 5, 6] should return [1, 7, 8] -> [2, 5, 6]
+ EXPECT_TRUE(dynamicControl().OptimizeOperation("foo", iop, &optimized));
+ ASSERT_EQ(2, optimized.src_extents_size());
+ ASSERT_EQ(2, optimized.dst_extents_size());
+ EXPECT_EQ(1u, optimized.src_extents(0).start_block());
+ EXPECT_EQ(1u, optimized.src_extents(0).num_blocks());
+ EXPECT_EQ(2u, optimized.dst_extents(0).start_block());
+ EXPECT_EQ(1u, optimized.dst_extents(0).num_blocks());
+ EXPECT_EQ(7u, optimized.src_extents(1).start_block());
+ EXPECT_EQ(2u, optimized.src_extents(1).num_blocks());
+ EXPECT_EQ(5u, optimized.dst_extents(1).start_block());
+ EXPECT_EQ(2u, optimized.dst_extents(1).num_blocks());
+
+ // Don't skip for static partitions.
+ EXPECT_FALSE(dynamicControl().OptimizeOperation("bar", iop, &optimized));
+}
+
+TEST_F(DynamicPartitionControlAndroidTest, ResetUpdate) {
+ MockPrefs prefs;
+ ASSERT_TRUE(dynamicControl().ResetUpdate(&prefs));
+}
+
+TEST_F(DynamicPartitionControlAndroidTest, IsAvbNotEnabledInFstab) {
+ std::string fstab_content =
+ "system /postinstall ext4 ro,nosuid,nodev,noexec "
+ "slotselect_other,logical\n"
+ "/dev/block/by-name/system /postinstall ext4 "
+ "ro,nosuid,nodev,noexec slotselect_other\n";
+ ScopedTempFile fstab;
+ ASSERT_TRUE(test_utils::WriteFileString(fstab.path(), fstab_content));
+ ASSERT_THAT(dynamicControl().RealIsAvbEnabledInFstab(fstab.path()),
+ Optional(false));
+}
+
+TEST_F(DynamicPartitionControlAndroidTest, IsAvbEnabledInFstab) {
+ std::string fstab_content =
+ "system /postinstall ext4 ro,nosuid,nodev,noexec "
+ "slotselect_other,logical,avb_keys=/foo\n";
+ ScopedTempFile fstab;
+ ASSERT_TRUE(test_utils::WriteFileString(fstab.path(), fstab_content));
+ ASSERT_THAT(dynamicControl().RealIsAvbEnabledInFstab(fstab.path()),
+ Optional(true));
+}
+
+TEST_P(DynamicPartitionControlAndroidTestP, AvbNotEnabledOnSystemOther) {
+ ON_CALL(dynamicControl(), GetSystemOtherPath(_, _, _, _, _))
+ .WillByDefault(Invoke([&](auto source_slot,
+ auto target_slot,
+ const auto& name,
+ auto path,
+ auto should_unmap) {
+ return dynamicControl().RealGetSystemOtherPath(
+ source_slot, target_slot, name, path, should_unmap);
+ }));
+ ON_CALL(dynamicControl(), IsAvbEnabledOnSystemOther())
+ .WillByDefault(Return(false));
+ EXPECT_TRUE(
+ dynamicControl().RealEraseSystemOtherAvbFooter(source(), target()));
+}
+
+TEST_P(DynamicPartitionControlAndroidTestP, NoSystemOtherToErase) {
+ SetMetadata(source(), {{S("system"), 100_MiB}});
+ ON_CALL(dynamicControl(), IsAvbEnabledOnSystemOther())
+ .WillByDefault(Return(true));
+ std::string path;
+ bool should_unmap;
+ ASSERT_TRUE(dynamicControl().RealGetSystemOtherPath(
+ source(), target(), T("system"), &path, &should_unmap));
+ ASSERT_TRUE(path.empty()) << path;
+ ASSERT_FALSE(should_unmap);
+ ON_CALL(dynamicControl(), GetSystemOtherPath(_, _, _, _, _))
+ .WillByDefault(Invoke([&](auto source_slot,
+ auto target_slot,
+ const auto& name,
+ auto path,
+ auto should_unmap) {
+ return dynamicControl().RealGetSystemOtherPath(
+ source_slot, target_slot, name, path, should_unmap);
+ }));
+ EXPECT_TRUE(
+ dynamicControl().RealEraseSystemOtherAvbFooter(source(), target()));
+}
+
+TEST_P(DynamicPartitionControlAndroidTestP, SkipEraseUpdatedSystemOther) {
+ PartitionSuffixSizes sizes{{S("system"), 100_MiB}, {T("system"), 100_MiB}};
+ SetMetadata(source(), sizes, LP_PARTITION_ATTR_UPDATED);
+ ON_CALL(dynamicControl(), IsAvbEnabledOnSystemOther())
+ .WillByDefault(Return(true));
+ std::string path;
+ bool should_unmap;
+ ASSERT_TRUE(dynamicControl().RealGetSystemOtherPath(
+ source(), target(), T("system"), &path, &should_unmap));
+ ASSERT_TRUE(path.empty()) << path;
+ ASSERT_FALSE(should_unmap);
+ ON_CALL(dynamicControl(), GetSystemOtherPath(_, _, _, _, _))
+ .WillByDefault(Invoke([&](auto source_slot,
+ auto target_slot,
+ const auto& name,
+ auto path,
+ auto should_unmap) {
+ return dynamicControl().RealGetSystemOtherPath(
+ source_slot, target_slot, name, path, should_unmap);
+ }));
+ EXPECT_TRUE(
+ dynamicControl().RealEraseSystemOtherAvbFooter(source(), target()));
+}
+
+TEST_P(DynamicPartitionControlAndroidTestP, EraseSystemOtherAvbFooter) {
+ constexpr uint64_t file_size = 1_MiB;
+ static_assert(file_size > AVB_FOOTER_SIZE);
+ ScopedTempFile system_other;
+ brillo::Blob original(file_size, 'X');
+ ASSERT_TRUE(test_utils::WriteFileVector(system_other.path(), original));
+ std::string mnt_path;
+ ScopedLoopbackDeviceBinder dev(system_other.path(), true, &mnt_path);
+ ASSERT_TRUE(dev.is_bound());
+
+ brillo::Blob device_content;
+ ASSERT_TRUE(utils::ReadFile(mnt_path, &device_content));
+ ASSERT_EQ(original, device_content);
+
+ PartitionSuffixSizes sizes{{S("system"), 100_MiB}, {T("system"), file_size}};
+ SetMetadata(source(), sizes);
+ ON_CALL(dynamicControl(), IsAvbEnabledOnSystemOther())
+ .WillByDefault(Return(true));
+ EXPECT_CALL(dynamicControl(),
+ GetSystemOtherPath(source(), target(), T("system"), _, _))
+ .WillRepeatedly(
+ Invoke([&](auto, auto, const auto&, auto path, auto should_unmap) {
+ *path = mnt_path;
+ *should_unmap = false;
+ return true;
+ }));
+ ASSERT_TRUE(
+ dynamicControl().RealEraseSystemOtherAvbFooter(source(), target()));
+
+ device_content.clear();
+ ASSERT_TRUE(utils::ReadFile(mnt_path, &device_content));
+ brillo::Blob new_expected(original);
+ // Clear the last AVB_FOOTER_SIZE bytes.
+ new_expected.resize(file_size - AVB_FOOTER_SIZE);
+ new_expected.resize(file_size, '\0');
+ ASSERT_EQ(new_expected, device_content);
+}
+
+class FakeAutoDevice : public android::snapshot::AutoDevice {
+ public:
+ FakeAutoDevice() : AutoDevice("") {}
+};
+
+class SnapshotPartitionTestP : public DynamicPartitionControlAndroidTestP {
+ public:
+ void SetUp() override {
+ DynamicPartitionControlAndroidTestP::SetUp();
+ ON_CALL(dynamicControl(), GetVirtualAbFeatureFlag())
+ .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::LAUNCH)));
+
+ snapshot_ = new NiceMock<MockSnapshotManager>();
+ dynamicControl().snapshot_.reset(snapshot_); // takes ownership
+ EXPECT_CALL(*snapshot_, BeginUpdate()).WillOnce(Return(true));
+ EXPECT_CALL(*snapshot_, EnsureMetadataMounted())
+ .WillRepeatedly(
+ Invoke([]() { return std::make_unique<FakeAutoDevice>(); }));
+
+ manifest_ =
+ PartitionSizesToManifest({{"system", 3_GiB}, {"vendor", 1_GiB}});
+ }
+ void ExpectCreateUpdateSnapshots(android::snapshot::Return val) {
+ manifest_.mutable_dynamic_partition_metadata()->set_snapshot_enabled(true);
+ EXPECT_CALL(*snapshot_, CreateUpdateSnapshots(_))
+ .WillRepeatedly(Invoke([&, val](const auto& manifest) {
+ // Deep comparison requires full protobuf library. Comparing the
+ // pointers are sufficient.
+ EXPECT_EQ(&manifest_, &manifest);
+ LOG(WARNING) << "CreateUpdateSnapshots returning " << val.string();
+ return val;
+ }));
+ }
+ bool PreparePartitionsForUpdate(uint64_t* required_size) {
+ return dynamicControl().PreparePartitionsForUpdate(
+ source(), target(), manifest_, true /* update */, required_size);
+ }
+ MockSnapshotManager* snapshot_ = nullptr;
+ DeltaArchiveManifest manifest_;
+};
+
+// Test happy path of PreparePartitionsForUpdate on a Virtual A/B device.
+TEST_P(SnapshotPartitionTestP, PreparePartitions) {
+ ExpectCreateUpdateSnapshots(android::snapshot::Return::Ok());
+ uint64_t required_size = 0;
+ EXPECT_TRUE(PreparePartitionsForUpdate(&required_size));
+ EXPECT_EQ(0u, required_size);
+}
+
+// Test that if not enough space, required size returned by SnapshotManager is
+// passed up.
+TEST_P(SnapshotPartitionTestP, PreparePartitionsNoSpace) {
+ ExpectCreateUpdateSnapshots(android::snapshot::Return::NoSpace(1_GiB));
+ uint64_t required_size = 0;
+ EXPECT_FALSE(PreparePartitionsForUpdate(&required_size));
+ EXPECT_EQ(1_GiB, required_size);
+}
+
+// Test that in recovery, use empty space in super partition for a snapshot
+// update first.
+TEST_P(SnapshotPartitionTestP, RecoveryUseSuperEmpty) {
+ ExpectCreateUpdateSnapshots(android::snapshot::Return::Ok());
+ EXPECT_CALL(dynamicControl(), IsRecovery()).WillRepeatedly(Return(true));
+ // Must not call PrepareDynamicPartitionsForUpdate if
+ // PrepareSnapshotPartitionsForUpdate succeeds.
+ EXPECT_CALL(dynamicControl(), PrepareDynamicPartitionsForUpdate(_, _, _, _))
+ .Times(0);
+ uint64_t required_size = 0;
+ EXPECT_TRUE(PreparePartitionsForUpdate(&required_size));
+ EXPECT_EQ(0u, required_size);
+}
+
+// Test that in recovery, if CreateUpdateSnapshots throws an error, try
+// the flashing path for full updates.
+TEST_P(SnapshotPartitionTestP, RecoveryErrorShouldDeleteSource) {
+ // Expectation on PreparePartitionsForUpdate
+ ExpectCreateUpdateSnapshots(android::snapshot::Return::NoSpace(1_GiB));
+ EXPECT_CALL(dynamicControl(), IsRecovery()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*snapshot_, CancelUpdate()).WillOnce(Return(true));
+ EXPECT_CALL(dynamicControl(), PrepareDynamicPartitionsForUpdate(_, _, _, _))
+ .WillRepeatedly(Invoke([&](auto source_slot,
+ auto target_slot,
+ const auto& manifest,
+ auto delete_source) {
+ EXPECT_EQ(source(), source_slot);
+ EXPECT_EQ(target(), target_slot);
+ // Deep comparison requires full protobuf library. Comparing the
+ // pointers are sufficient.
+ EXPECT_EQ(&manifest_, &manifest);
+ EXPECT_TRUE(delete_source);
+ return dynamicControl().RealPrepareDynamicPartitionsForUpdate(
+ source_slot, target_slot, manifest, delete_source);
+ }));
+ // Only one slot of space in super
+ uint64_t super_size = kDefaultGroupSize + 1_MiB;
+ // Expectation on PrepareDynamicPartitionsForUpdate
+ SetMetadata(
+ source(), {{S("system"), 2_GiB}, {S("vendor"), 1_GiB}}, 0, super_size);
+ ExpectUnmap({T("system"), T("vendor")});
+ // Expect that the source partitions aren't present in target super metadata.
+ ExpectStoreMetadata({{T("system"), 3_GiB}, {T("vendor"), 1_GiB}});
+
+ uint64_t required_size = 0;
+ EXPECT_TRUE(PreparePartitionsForUpdate(&required_size));
+ EXPECT_EQ(0u, required_size);
+}
+
+INSTANTIATE_TEST_CASE_P(DynamicPartitionControlAndroidTest,
+ SnapshotPartitionTestP,
+ testing::Values(TestParam{0, 1}, TestParam{1, 0}));
+
+} // namespace chromeos_update_engine
diff --git a/aosp/dynamic_partition_test_utils.h b/aosp/dynamic_partition_test_utils.h
new file mode 100644
index 0000000..c7be1cb
--- /dev/null
+++ b/aosp/dynamic_partition_test_utils.h
@@ -0,0 +1,288 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_AOSP_DYNAMIC_PARTITION_TEST_UTILS_H_
+#define UPDATE_ENGINE_AOSP_DYNAMIC_PARTITION_TEST_UTILS_H_
+
+#include <stdint.h>
+
+#include <iostream>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <base/strings/string_util.h>
+#include <fs_mgr.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <liblp/builder.h>
+#include <storage_literals/storage_literals.h>
+
+#include "update_engine/common/boot_control_interface.h"
+#include "update_engine/update_metadata.pb.h"
+
+namespace chromeos_update_engine {
+
+using android::fs_mgr::MetadataBuilder;
+using testing::_;
+using testing::MakeMatcher;
+using testing::Matcher;
+using testing::MatcherInterface;
+using testing::MatchResultListener;
+using namespace android::storage_literals; // NOLINT(build/namespaces)
+
+constexpr const uint32_t kMaxNumSlots = 2;
+constexpr const char* kSlotSuffixes[kMaxNumSlots] = {"_a", "_b"};
+constexpr const char* kFakeDevicePath = "/fake/dev/path/";
+constexpr const char* kFakeDmDevicePath = "/fake/dm/dev/path/";
+constexpr const uint32_t kFakeMetadataSize = 65536;
+constexpr const char* kDefaultGroup = "foo";
+constexpr const char* kFakeSuper = "fake_super";
+
+// A map describing the size of each partition.
+// "{name, size}"
+using PartitionSizes = std::map<std::string, uint64_t>;
+
+// "{name_a, size}"
+using PartitionSuffixSizes = std::map<std::string, uint64_t>;
+
+constexpr uint64_t kDefaultGroupSize = 5_GiB;
+// Super device size. 1 MiB for metadata.
+constexpr uint64_t kDefaultSuperSize = kDefaultGroupSize * 2 + 1_MiB;
+
+template <typename U, typename V>
+inline std::ostream& operator<<(std::ostream& os, const std::map<U, V>& param) {
+ os << "{";
+ bool first = true;
+ for (const auto& pair : param) {
+ if (!first)
+ os << ", ";
+ os << pair.first << ":" << pair.second;
+ first = false;
+ }
+ return os << "}";
+}
+
+template <typename V>
+inline void VectorToStream(std::ostream& os, const V& param) {
+ os << "[";
+ bool first = true;
+ for (const auto& e : param) {
+ if (!first)
+ os << ", ";
+ os << e;
+ first = false;
+ }
+ os << "]";
+}
+
+inline std::ostream& operator<<(std::ostream& os, const PartitionUpdate& p) {
+ return os << "{" << p.partition_name() << ", "
+ << p.new_partition_info().size() << "}";
+}
+
+inline std::ostream& operator<<(std::ostream& os,
+ const DynamicPartitionGroup& g) {
+ os << "{" << g.name() << ", " << g.size() << ", ";
+ VectorToStream(os, g.partition_names());
+ return os << "}";
+}
+
+inline std::ostream& operator<<(std::ostream& os,
+ const DeltaArchiveManifest& m) {
+ os << "{.groups = ";
+ VectorToStream(os, m.dynamic_partition_metadata().groups());
+ os << ", .partitions = ";
+ VectorToStream(os, m.partitions());
+ return os;
+}
+
+inline std::string GetDevice(const std::string& name) {
+ return kFakeDevicePath + name;
+}
+
+inline std::string GetDmDevice(const std::string& name) {
+ return kFakeDmDevicePath + name;
+}
+
+inline DynamicPartitionGroup* AddGroup(DeltaArchiveManifest* manifest,
+ const std::string& group,
+ uint64_t group_size) {
+ auto* g = manifest->mutable_dynamic_partition_metadata()->add_groups();
+ g->set_name(group);
+ g->set_size(group_size);
+ return g;
+}
+
+inline void AddPartition(DeltaArchiveManifest* manifest,
+ DynamicPartitionGroup* group,
+ const std::string& partition,
+ uint64_t partition_size) {
+ group->add_partition_names(partition);
+ auto* p = manifest->add_partitions();
+ p->set_partition_name(partition);
+ p->mutable_new_partition_info()->set_size(partition_size);
+}
+
+// To support legacy tests, auto-convert {name_a: size} map to
+// DeltaArchiveManifest.
+inline DeltaArchiveManifest PartitionSuffixSizesToManifest(
+ const PartitionSuffixSizes& partition_sizes) {
+ DeltaArchiveManifest manifest;
+ for (const char* suffix : kSlotSuffixes) {
+ AddGroup(&manifest, std::string(kDefaultGroup) + suffix, kDefaultGroupSize);
+ }
+ for (const auto& pair : partition_sizes) {
+ for (size_t suffix_idx = 0; suffix_idx < kMaxNumSlots; ++suffix_idx) {
+ if (base::EndsWith(pair.first,
+ kSlotSuffixes[suffix_idx],
+ base::CompareCase::SENSITIVE)) {
+ AddPartition(
+ &manifest,
+ manifest.mutable_dynamic_partition_metadata()->mutable_groups(
+ suffix_idx),
+ pair.first,
+ pair.second);
+ }
+ }
+ }
+ return manifest;
+}
+
+// To support legacy tests, auto-convert {name: size} map to PartitionMetadata.
+inline DeltaArchiveManifest PartitionSizesToManifest(
+ const PartitionSizes& partition_sizes) {
+ DeltaArchiveManifest manifest;
+ auto* g = AddGroup(&manifest, std::string(kDefaultGroup), kDefaultGroupSize);
+ for (const auto& pair : partition_sizes) {
+ AddPartition(&manifest, g, pair.first, pair.second);
+ }
+ return manifest;
+}
+
+inline std::unique_ptr<MetadataBuilder> NewFakeMetadata(
+ const DeltaArchiveManifest& manifest,
+ uint32_t partition_attr = 0,
+ uint64_t super_size = kDefaultSuperSize) {
+ auto builder =
+ MetadataBuilder::New(super_size, kFakeMetadataSize, kMaxNumSlots);
+ for (const auto& group : manifest.dynamic_partition_metadata().groups()) {
+ EXPECT_TRUE(builder->AddGroup(group.name(), group.size()));
+ for (const auto& partition_name : group.partition_names()) {
+ EXPECT_NE(
+ nullptr,
+ builder->AddPartition(partition_name, group.name(), partition_attr));
+ }
+ }
+ for (const auto& partition : manifest.partitions()) {
+ auto p = builder->FindPartition(partition.partition_name());
+ EXPECT_TRUE(p && builder->ResizePartition(
+ p, partition.new_partition_info().size()));
+ }
+ return builder;
+}
+
+class MetadataMatcher : public MatcherInterface<MetadataBuilder*> {
+ public:
+ explicit MetadataMatcher(const PartitionSuffixSizes& partition_sizes)
+ : manifest_(PartitionSuffixSizesToManifest(partition_sizes)) {}
+ explicit MetadataMatcher(const DeltaArchiveManifest& manifest)
+ : manifest_(manifest) {}
+
+ bool MatchAndExplain(MetadataBuilder* metadata,
+ MatchResultListener* listener) const override {
+ bool success = true;
+ for (const auto& group : manifest_.dynamic_partition_metadata().groups()) {
+ for (const auto& partition_name : group.partition_names()) {
+ auto p = metadata->FindPartition(partition_name);
+ if (p == nullptr) {
+ if (!success)
+ *listener << "; ";
+ *listener << "No partition " << partition_name;
+ success = false;
+ continue;
+ }
+ const auto& partition_updates = manifest_.partitions();
+ auto it = std::find_if(partition_updates.begin(),
+ partition_updates.end(),
+ [&](const auto& p) {
+ return p.partition_name() == partition_name;
+ });
+ if (it == partition_updates.end()) {
+ *listener << "Can't find partition update " << partition_name;
+ success = false;
+ continue;
+ }
+ auto partition_size = it->new_partition_info().size();
+ if (p->size() != partition_size) {
+ if (!success)
+ *listener << "; ";
+ *listener << "Partition " << partition_name << " has size "
+ << p->size() << ", expected " << partition_size;
+ success = false;
+ }
+ if (p->group_name() != group.name()) {
+ if (!success)
+ *listener << "; ";
+ *listener << "Partition " << partition_name << " has group "
+ << p->group_name() << ", expected " << group.name();
+ success = false;
+ }
+ }
+ }
+ return success;
+ }
+
+ void DescribeTo(std::ostream* os) const override {
+ *os << "expect: " << manifest_;
+ }
+
+ void DescribeNegationTo(std::ostream* os) const override {
+ *os << "expect not: " << manifest_;
+ }
+
+ private:
+ DeltaArchiveManifest manifest_;
+};
+
+inline Matcher<MetadataBuilder*> MetadataMatches(
+ const PartitionSuffixSizes& partition_sizes) {
+ return MakeMatcher(new MetadataMatcher(partition_sizes));
+}
+
+inline Matcher<MetadataBuilder*> MetadataMatches(
+ const DeltaArchiveManifest& manifest) {
+ return MakeMatcher(new MetadataMatcher(manifest));
+}
+
+MATCHER_P(HasGroup, group, " has group " + group) {
+ auto groups = arg->ListGroups();
+ return std::find(groups.begin(), groups.end(), group) != groups.end();
+}
+
+struct TestParam {
+ uint32_t source;
+ uint32_t target;
+};
+inline std::ostream& operator<<(std::ostream& os, const TestParam& param) {
+ return os << "{source: " << param.source << ", target:" << param.target
+ << "}";
+}
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_AOSP_DYNAMIC_PARTITION_TEST_UTILS_H_
diff --git a/aosp/dynamic_partition_utils.cc b/aosp/dynamic_partition_utils.cc
new file mode 100644
index 0000000..6b77a45
--- /dev/null
+++ b/aosp/dynamic_partition_utils.cc
@@ -0,0 +1,39 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/aosp/dynamic_partition_utils.h"
+
+#include <vector>
+
+#include <base/logging.h>
+#include <base/strings/string_util.h>
+
+using android::fs_mgr::MetadataBuilder;
+
+namespace chromeos_update_engine {
+
+void DeleteGroupsWithSuffix(MetadataBuilder* builder,
+ const std::string& suffix) {
+ std::vector<std::string> groups = builder->ListGroups();
+ for (const auto& group_name : groups) {
+ if (base::EndsWith(group_name, suffix, base::CompareCase::SENSITIVE)) {
+ LOG(INFO) << "Removing group " << group_name;
+ builder->RemoveGroupAndPartitions(group_name);
+ }
+ }
+}
+
+} // namespace chromeos_update_engine
diff --git a/aosp/dynamic_partition_utils.h b/aosp/dynamic_partition_utils.h
new file mode 100644
index 0000000..5a51d5e
--- /dev/null
+++ b/aosp/dynamic_partition_utils.h
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_AOSP_DYNAMIC_PARTITION_UTILS_H_
+#define UPDATE_ENGINE_AOSP_DYNAMIC_PARTITION_UTILS_H_
+
+#include <string>
+
+#include <liblp/builder.h>
+
+namespace chromeos_update_engine {
+
+// Delete all groups (and their partitions) in |builder| that have names
+// ending with |suffix|.
+void DeleteGroupsWithSuffix(android::fs_mgr::MetadataBuilder* builder,
+ const std::string& suffix);
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_AOSP_DYNAMIC_PARTITION_UTILS_H_
diff --git a/aosp/hardware_android.cc b/aosp/hardware_android.cc
new file mode 100644
index 0000000..6f884d4
--- /dev/null
+++ b/aosp/hardware_android.cc
@@ -0,0 +1,283 @@
+//
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/aosp/hardware_android.h"
+
+#include <sys/types.h>
+
+#include <memory>
+#include <string>
+#include <string_view>
+
+#include <android/sysprop/GkiProperties.sysprop.h>
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <base/files/file_util.h>
+#include <bootloader_message/bootloader_message.h>
+
+#include "update_engine/common/error_code_utils.h"
+#include "update_engine/common/hardware.h"
+#include "update_engine/common/platform_constants.h"
+#include "update_engine/common/utils.h"
+
+using android::base::GetBoolProperty;
+using android::base::GetIntProperty;
+using android::base::GetProperty;
+using std::string;
+
+namespace chromeos_update_engine {
+
+namespace {
+
+// Android properties that identify the hardware and potentially non-updatable
+// parts of the bootloader (such as the bootloader version and the baseband
+// version).
+const char kPropProductManufacturer[] = "ro.product.manufacturer";
+const char kPropBootHardwareSKU[] = "ro.boot.hardware.sku";
+const char kPropBootRevision[] = "ro.boot.revision";
+const char kPropBuildDateUTC[] = "ro.build.date.utc";
+
+string GetPartitionBuildDate(const string& partition_name) {
+ return android::base::GetProperty("ro." + partition_name + ".build.date.utc",
+ "");
+}
+
+ErrorCode IsTimestampNewerLogged(const std::string& partition_name,
+ const std::string& old_version,
+ const std::string& new_version) {
+ auto error_code = utils::IsTimestampNewer(old_version, new_version);
+ if (error_code != ErrorCode::kSuccess) {
+ LOG(WARNING) << "Timestamp check failed with "
+ << utils::ErrorCodeToString(error_code) << ": "
+ << partition_name << " Partition timestamp: " << old_version
+ << " Update timestamp: " << new_version;
+ }
+ return error_code;
+}
+
+} // namespace
+
+namespace hardware {
+
+// Factory defined in hardware.h.
+std::unique_ptr<HardwareInterface> CreateHardware() {
+ return std::make_unique<HardwareAndroid>();
+}
+
+} // namespace hardware
+
+// In Android there are normally three kinds of builds: eng, userdebug and user.
+// These builds target respectively a developer build, a debuggable version of
+// the final product and the pristine final product the end user will run.
+// Apart from the ro.build.type property name, they differ in the following
+// properties that characterize the builds:
+// * eng builds: ro.secure=0 and ro.debuggable=1
+// * userdebug builds: ro.secure=1 and ro.debuggable=1
+// * user builds: ro.secure=1 and ro.debuggable=0
+//
+// See IsOfficialBuild() and IsNormalMode() for the meaning of these options in
+// Android.
+
+bool HardwareAndroid::IsOfficialBuild() const {
+ // We run an official build iff ro.secure == 1, because we expect the build to
+ // behave like the end user product and check for updates. Note that while
+ // developers are able to build "official builds" by just running "make user",
+ // that will only result in a more restrictive environment. The important part
+ // is that we don't produce and push "non-official" builds to the end user.
+ //
+ // In case of a non-bool value, we take the most restrictive option and
+ // assume we are in an official-build.
+ return GetBoolProperty("ro.secure", true);
+}
+
+bool HardwareAndroid::IsNormalBootMode() const {
+ // We are running in "dev-mode" iff ro.debuggable == 1. In dev-mode the
+ // update_engine will allow extra developers options, such as providing a
+ // different update URL. In case of error, we assume the build is in
+ // normal-mode.
+ return !GetBoolProperty("ro.debuggable", false);
+}
+
+bool HardwareAndroid::AreDevFeaturesEnabled() const {
+ return !IsNormalBootMode();
+}
+
+bool HardwareAndroid::IsOOBEEnabled() const {
+ // No OOBE flow blocking updates for Android-based boards.
+ return false;
+}
+
+bool HardwareAndroid::IsOOBEComplete(base::Time* out_time_of_oobe) const {
+ LOG(WARNING) << "OOBE is not enabled but IsOOBEComplete() called.";
+ if (out_time_of_oobe)
+ *out_time_of_oobe = base::Time();
+ return true;
+}
+
+string HardwareAndroid::GetHardwareClass() const {
+ auto manufacturer = GetProperty(kPropProductManufacturer, "");
+ auto sku = GetProperty(kPropBootHardwareSKU, "");
+ auto revision = GetProperty(kPropBootRevision, "");
+
+ return manufacturer + ":" + sku + ":" + revision;
+}
+
+string HardwareAndroid::GetDeviceRequisition() const {
+ LOG(WARNING) << "STUB: Getting requisition is not supported.";
+ return "";
+}
+
+int HardwareAndroid::GetMinKernelKeyVersion() const {
+ LOG(WARNING) << "STUB: No Kernel key version is available.";
+ return -1;
+}
+
+int HardwareAndroid::GetMinFirmwareKeyVersion() const {
+ LOG(WARNING) << "STUB: No Firmware key version is available.";
+ return -1;
+}
+
+int HardwareAndroid::GetMaxFirmwareKeyRollforward() const {
+ LOG(WARNING) << "STUB: Getting firmware_max_rollforward is not supported.";
+ return -1;
+}
+
+bool HardwareAndroid::SetMaxFirmwareKeyRollforward(
+ int firmware_max_rollforward) {
+ LOG(WARNING) << "STUB: Setting firmware_max_rollforward is not supported.";
+ return false;
+}
+
+bool HardwareAndroid::SetMaxKernelKeyRollforward(int kernel_max_rollforward) {
+ LOG(WARNING) << "STUB: Setting kernel_max_rollforward is not supported.";
+ return false;
+}
+
+int HardwareAndroid::GetPowerwashCount() const {
+ LOG(WARNING) << "STUB: Assuming no factory reset was performed.";
+ return 0;
+}
+
+bool HardwareAndroid::SchedulePowerwash(bool save_rollback_data) {
+ LOG(INFO) << "Scheduling a powerwash to BCB.";
+ LOG_IF(WARNING, save_rollback_data) << "save_rollback_data was true but "
+ << "isn't supported.";
+ string err;
+ if (!update_bootloader_message({"--wipe_data", "--reason=wipe_data_from_ota"},
+ &err)) {
+ LOG(ERROR) << "Failed to update bootloader message: " << err;
+ return false;
+ }
+ return true;
+}
+
+bool HardwareAndroid::CancelPowerwash() {
+ string err;
+ if (!clear_bootloader_message(&err)) {
+ LOG(ERROR) << "Failed to clear bootloader message: " << err;
+ return false;
+ }
+ return true;
+}
+
+bool HardwareAndroid::GetNonVolatileDirectory(base::FilePath* path) const {
+ base::FilePath local_path(constants::kNonVolatileDirectory);
+ if (!base::DirectoryExists(local_path)) {
+ LOG(ERROR) << "Non-volatile directory not found: " << local_path.value();
+ return false;
+ }
+ *path = local_path;
+ return true;
+}
+
+bool HardwareAndroid::GetPowerwashSafeDirectory(base::FilePath* path) const {
+ // On Android, we don't have a directory persisted across powerwash.
+ return false;
+}
+
+int64_t HardwareAndroid::GetBuildTimestamp() const {
+ return GetIntProperty<int64_t>(kPropBuildDateUTC, 0);
+}
+
+// Returns true if the device runs an userdebug build, and explicitly allows OTA
+// downgrade.
+bool HardwareAndroid::AllowDowngrade() const {
+ return GetBoolProperty("ro.ota.allow_downgrade", false) &&
+ GetBoolProperty("ro.debuggable", false);
+}
+
+bool HardwareAndroid::GetFirstActiveOmahaPingSent() const {
+ LOG(WARNING) << "STUB: Assuming first active omaha was never set.";
+ return false;
+}
+
+bool HardwareAndroid::SetFirstActiveOmahaPingSent() {
+ LOG(WARNING) << "STUB: Assuming first active omaha is set.";
+ // We will set it true, so its failure doesn't cause escalation.
+ return true;
+}
+
+void HardwareAndroid::SetWarmReset(bool warm_reset) {
+ if constexpr (!constants::kIsRecovery) {
+ constexpr char warm_reset_prop[] = "ota.warm_reset";
+ if (!android::base::SetProperty(warm_reset_prop, warm_reset ? "1" : "0")) {
+ LOG(WARNING) << "Failed to set prop " << warm_reset_prop;
+ }
+ }
+}
+
+string HardwareAndroid::GetVersionForLogging(
+ const string& partition_name) const {
+ if (partition_name == "boot") {
+ // ro.bootimage.build.date.utc
+ return GetPartitionBuildDate("bootimage");
+ }
+ return GetPartitionBuildDate(partition_name);
+}
+
+ErrorCode HardwareAndroid::IsPartitionUpdateValid(
+ const string& partition_name, const string& new_version) const {
+ if (partition_name == "boot") {
+ const auto old_version = GetPartitionBuildDate("bootimage");
+ auto error_code =
+ IsTimestampNewerLogged(partition_name, old_version, new_version);
+ if (error_code == ErrorCode::kPayloadTimestampError) {
+ bool prevent_downgrade =
+ android::sysprop::GkiProperties::prevent_downgrade_version().value_or(
+ false);
+ if (!prevent_downgrade) {
+ LOG(WARNING) << "Downgrade of boot image is detected, but permitting "
+ "update because device does not prevent boot image "
+ "downgrade";
+ // If prevent_downgrade_version sysprop is not explicitly set, permit
+ // downgrade in boot image version.
+ // Even though error_code is overridden here, always call
+ // IsTimestampNewerLogged to produce log messages.
+ error_code = ErrorCode::kSuccess;
+ }
+ }
+ return error_code;
+ }
+
+ const auto old_version = GetPartitionBuildDate(partition_name);
+ // TODO(zhangkelvin) for some partitions, missing a current timestamp should
+ // be an error, e.g. system, vendor, product etc.
+ auto error_code =
+ IsTimestampNewerLogged(partition_name, old_version, new_version);
+ return error_code;
+}
+
+} // namespace chromeos_update_engine
diff --git a/aosp/hardware_android.h b/aosp/hardware_android.h
new file mode 100644
index 0000000..5ffd7c5
--- /dev/null
+++ b/aosp/hardware_android.h
@@ -0,0 +1,73 @@
+//
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_AOSP_HARDWARE_ANDROID_H_
+#define UPDATE_ENGINE_AOSP_HARDWARE_ANDROID_H_
+
+#include <string>
+#include <string_view>
+
+#include <base/macros.h>
+#include <base/time/time.h>
+
+#include "update_engine/common/error_code.h"
+#include "update_engine/common/hardware.h"
+#include "update_engine/common/hardware_interface.h"
+
+namespace chromeos_update_engine {
+
+// Implements the real interface with the hardware in the Android platform.
+class HardwareAndroid : public HardwareInterface {
+ public:
+ HardwareAndroid() = default;
+ ~HardwareAndroid() override = default;
+
+ // HardwareInterface methods.
+ bool IsOfficialBuild() const override;
+ bool IsNormalBootMode() const override;
+ bool AreDevFeaturesEnabled() const override;
+ bool IsOOBEEnabled() const override;
+ bool IsOOBEComplete(base::Time* out_time_of_oobe) const override;
+ std::string GetHardwareClass() const override;
+ std::string GetDeviceRequisition() const override;
+ int GetMinKernelKeyVersion() const override;
+ int GetMinFirmwareKeyVersion() const override;
+ int GetMaxFirmwareKeyRollforward() const override;
+ bool SetMaxFirmwareKeyRollforward(int firmware_max_rollforward) override;
+ bool SetMaxKernelKeyRollforward(int kernel_max_rollforward) override;
+ int GetPowerwashCount() const override;
+ bool SchedulePowerwash(bool save_rollback_data) override;
+ bool CancelPowerwash() override;
+ bool GetNonVolatileDirectory(base::FilePath* path) const override;
+ bool GetPowerwashSafeDirectory(base::FilePath* path) const override;
+ int64_t GetBuildTimestamp() const override;
+ bool AllowDowngrade() const override;
+ bool GetFirstActiveOmahaPingSent() const override;
+ bool SetFirstActiveOmahaPingSent() override;
+ void SetWarmReset(bool warm_reset) override;
+ [[nodiscard]] std::string GetVersionForLogging(
+ const std::string& partition_name) const override;
+ [[nodiscard]] ErrorCode IsPartitionUpdateValid(
+ const std::string& partition_name,
+ const std::string& new_version) const override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HardwareAndroid);
+};
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_AOSP_HARDWARE_ANDROID_H_
diff --git a/aosp/logging_android.cc b/aosp/logging_android.cc
new file mode 100644
index 0000000..0219075
--- /dev/null
+++ b/aosp/logging_android.cc
@@ -0,0 +1,276 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <functional>
+#include <iomanip>
+#include <string>
+#include <string_view>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <base/files/dir_reader_posix.h>
+#include <base/logging.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <log/log.h>
+
+#include "update_engine/common/utils.h"
+
+using std::string;
+
+#ifdef _UE_SIDELOAD
+constexpr bool kSideload = true;
+#else
+constexpr bool kSideload = false;
+#endif
+
+namespace chromeos_update_engine {
+namespace {
+
+constexpr char kSystemLogsRoot[] = "/data/misc/update_engine_log";
+constexpr size_t kLogCount = 5;
+
+// Keep the most recent |kLogCount| logs but remove the old ones in
+// "/data/misc/update_engine_log/".
+void DeleteOldLogs(const string& kLogsRoot) {
+ base::DirReaderPosix reader(kLogsRoot.c_str());
+ if (!reader.IsValid()) {
+ LOG(ERROR) << "Failed to read " << kLogsRoot;
+ return;
+ }
+
+ std::vector<string> old_logs;
+ while (reader.Next()) {
+ if (reader.name()[0] == '.')
+ continue;
+
+ // Log files are in format "update_engine.%Y%m%d-%H%M%S",
+ // e.g. update_engine.20090103-231425
+ uint64_t date;
+ uint64_t local_time;
+ if (sscanf(reader.name(),
+ "update_engine.%" PRIu64 "-%" PRIu64 "",
+ &date,
+ &local_time) == 2) {
+ old_logs.push_back(reader.name());
+ } else {
+ LOG(WARNING) << "Unrecognized log file " << reader.name();
+ }
+ }
+
+ std::sort(old_logs.begin(), old_logs.end(), std::greater<string>());
+ for (size_t i = kLogCount; i < old_logs.size(); i++) {
+ string log_path = kLogsRoot + "/" + old_logs[i];
+ if (unlink(log_path.c_str()) == -1) {
+ PLOG(WARNING) << "Failed to unlink " << log_path;
+ }
+ }
+}
+
+string SetupLogFile(const string& kLogsRoot) {
+ DeleteOldLogs(kLogsRoot);
+
+ return base::StringPrintf("%s/update_engine.%s",
+ kLogsRoot.c_str(),
+ utils::GetTimeAsString(::time(nullptr)).c_str());
+}
+
+const char* LogPriorityToCString(int priority) {
+ switch (priority) {
+ case ANDROID_LOG_VERBOSE:
+ return "VERBOSE";
+ case ANDROID_LOG_DEBUG:
+ return "DEBUG";
+ case ANDROID_LOG_INFO:
+ return "INFO";
+ case ANDROID_LOG_WARN:
+ return "WARN";
+ case ANDROID_LOG_ERROR:
+ return "ERROR";
+ case ANDROID_LOG_FATAL:
+ return "FATAL";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+using LoggerFunction = std::function<void(const struct __android_log_message*)>;
+
+class FileLogger {
+ public:
+ explicit FileLogger(const string& path) {
+ fd_.reset(TEMP_FAILURE_RETRY(
+ open(path.c_str(),
+ O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW | O_SYNC,
+ 0644)));
+ if (fd_ == -1) {
+ // Use ALOGE that logs to logd before __android_log_set_logger.
+ ALOGE("Cannot open persistent log %s: %s", path.c_str(), strerror(errno));
+ return;
+ }
+ // The log file will have AID_LOG as group ID; this GID is inherited from
+ // the parent directory "/data/misc/update_engine_log" which sets the SGID
+ // bit.
+ if (fchmod(fd_.get(), 0640) == -1) {
+ // Use ALOGE that logs to logd before __android_log_set_logger.
+ ALOGE("Cannot chmod 0640 persistent log %s: %s",
+ path.c_str(),
+ strerror(errno));
+ return;
+ }
+ }
+ // Copy-constructor needed to be converted to std::function.
+ FileLogger(const FileLogger& other) { fd_.reset(dup(other.fd_)); }
+ void operator()(const struct __android_log_message* log_message) {
+ if (fd_ == -1) {
+ return;
+ }
+
+ std::string_view message_str =
+ log_message->message != nullptr ? log_message->message : "";
+
+ WriteToFd(GetPrefix(log_message));
+ WriteToFd(message_str);
+ WriteToFd("\n");
+ }
+
+ private:
+ android::base::unique_fd fd_;
+ void WriteToFd(std::string_view message) {
+ ignore_result(
+ android::base::WriteFully(fd_, message.data(), message.size()));
+ }
+
+ string GetPrefix(const struct __android_log_message* log_message) {
+ std::stringstream ss;
+ timeval tv;
+ gettimeofday(&tv, nullptr);
+ time_t t = tv.tv_sec;
+ struct tm local_time;
+ localtime_r(&t, &local_time);
+ struct tm* tm_time = &local_time;
+ ss << "[" << std::setfill('0') << std::setw(2) << 1 + tm_time->tm_mon
+ << std::setw(2) << tm_time->tm_mday << '/' << std::setw(2)
+ << tm_time->tm_hour << std::setw(2) << tm_time->tm_min << std::setw(2)
+ << tm_time->tm_sec << '.' << std::setw(6) << tv.tv_usec << "] ";
+ // libchrome logs prepends |message| with severity, file and line, but
+ // leave logger_data->file as nullptr.
+ // libbase / liblog logs doesn't. Hence, add them to match the style.
+ // For liblog logs that doesn't set logger_data->file, not printing the
+ // priority is acceptable.
+ if (log_message->file) {
+ ss << "[" << LogPriorityToCString(log_message->priority) << ':'
+ << log_message->file << '(' << log_message->line << ")] ";
+ }
+ return ss.str();
+ }
+};
+
+class CombinedLogger {
+ public:
+ CombinedLogger(bool log_to_system, bool log_to_file) {
+ if (log_to_system) {
+ if (kSideload) {
+ // No logd in sideload. Use stdout.
+ // recovery has already redirected stdio properly.
+ loggers_.push_back(__android_log_stderr_logger);
+ } else {
+ loggers_.push_back(__android_log_logd_logger);
+ }
+ }
+ if (log_to_file) {
+ loggers_.push_back(std::move(FileLogger(SetupLogFile(kSystemLogsRoot))));
+ }
+ }
+ void operator()(const struct __android_log_message* log_message) {
+ for (auto&& logger : loggers_) {
+ logger(log_message);
+ }
+ }
+
+ private:
+ std::vector<LoggerFunction> loggers_;
+};
+
+// Redirect all libchrome logs to liblog using our custom handler that does
+// not call __android_log_write and explicitly write to stderr at the same
+// time. The preset CombinedLogger already writes to stderr properly.
+bool RedirectToLiblog(int severity,
+ const char* file,
+ int line,
+ size_t message_start,
+ const std::string& str_newline) {
+ android_LogPriority priority =
+ (severity < 0) ? ANDROID_LOG_VERBOSE : ANDROID_LOG_UNKNOWN;
+ switch (severity) {
+ case logging::LOG_INFO:
+ priority = ANDROID_LOG_INFO;
+ break;
+ case logging::LOG_WARNING:
+ priority = ANDROID_LOG_WARN;
+ break;
+ case logging::LOG_ERROR:
+ priority = ANDROID_LOG_ERROR;
+ break;
+ case logging::LOG_FATAL:
+ priority = ANDROID_LOG_FATAL;
+ break;
+ }
+ std::string_view sv = str_newline;
+ ignore_result(android::base::ConsumeSuffix(&sv, "\n"));
+ std::string str(sv.data(), sv.size());
+ // This will eventually be redirected to CombinedLogger.
+ // Use nullptr as tag so that liblog infers log tag from getprogname().
+ __android_log_write(priority, nullptr /* tag */, str.c_str());
+ return true;
+}
+
+} // namespace
+
+void SetupLogging(bool log_to_system, bool log_to_file) {
+ // Note that libchrome logging uses liblog.
+ // By calling liblog's __android_log_set_logger function, all of libchrome
+ // (used by update_engine) / libbase / liblog (used by depended modules)
+ // logging eventually redirects to CombinedLogger.
+ static auto g_logger =
+ std::make_unique<CombinedLogger>(log_to_system, log_to_file);
+ __android_log_set_logger([](const struct __android_log_message* log_message) {
+ (*g_logger)(log_message);
+ });
+
+ // libchrome logging should not log to file.
+ logging::LoggingSettings log_settings;
+ log_settings.lock_log = logging::DONT_LOCK_LOG_FILE;
+ log_settings.logging_dest =
+ static_cast<logging::LoggingDestination>(logging::LOG_NONE);
+ log_settings.log_file = nullptr;
+ logging::InitLogging(log_settings);
+ logging::SetLogItems(false /* enable_process_id */,
+ false /* enable_thread_id */,
+ false /* enable_timestamp */,
+ false /* enable_tickcount */);
+ logging::SetLogMessageHandler(&RedirectToLiblog);
+}
+
+} // namespace chromeos_update_engine
diff --git a/aosp/metrics_reporter_android.cc b/aosp/metrics_reporter_android.cc
new file mode 100644
index 0000000..ea3bb6d
--- /dev/null
+++ b/aosp/metrics_reporter_android.cc
@@ -0,0 +1,170 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/aosp/metrics_reporter_android.h"
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+
+#include <android-base/properties.h>
+#include <base/strings/string_util.h>
+#include <fs_mgr.h>
+#include <libdm/dm.h>
+#include <liblp/builder.h>
+#include <liblp/liblp.h>
+#include <statslog.h>
+
+#include "update_engine/common/constants.h"
+
+using android::fs_mgr::GetPartitionGroupName;
+using android::fs_mgr::LpMetadata;
+using android::fs_mgr::MetadataBuilder;
+using android::fs_mgr::ReadMetadata;
+using android::fs_mgr::SlotNumberForSlotSuffix;
+using base::EndsWith;
+
+namespace {
+// A number offset adds on top of the enum value. e.g. ErrorCode::SUCCESS will
+// be reported as 10000, and AttemptResult::UPDATE_CANCELED will be reported as
+// 10011. This keeps the ordering of update engine's enum definition when statsd
+// atoms reserve the value 0 for unknown state.
+constexpr auto kMetricsReporterEnumOffset = 10000;
+
+int32_t GetStatsdEnumValue(int32_t value) {
+ return kMetricsReporterEnumOffset + value;
+}
+} // namespace
+
+namespace chromeos_update_engine {
+
+namespace metrics {
+
+std::unique_ptr<MetricsReporterInterface> CreateMetricsReporter() {
+ return std::make_unique<MetricsReporterAndroid>();
+}
+
+} // namespace metrics
+
+void MetricsReporterAndroid::ReportUpdateAttemptMetrics(
+ SystemState* /* system_state */,
+ int attempt_number,
+ PayloadType payload_type,
+ base::TimeDelta duration,
+ base::TimeDelta duration_uptime,
+ int64_t payload_size,
+ metrics::AttemptResult attempt_result,
+ ErrorCode error_code) {
+ int64_t payload_size_mib = payload_size / kNumBytesInOneMiB;
+
+ int64_t super_partition_size_bytes = 0;
+ int64_t super_free_space = 0;
+ int64_t slot_size_bytes = 0;
+
+ if (android::base::GetBoolProperty("ro.boot.dynamic_partitions", false)) {
+ uint32_t slot = SlotNumberForSlotSuffix(fs_mgr_get_slot_suffix());
+ auto super_device = fs_mgr_get_super_partition_name();
+ std::unique_ptr<LpMetadata> metadata = ReadMetadata(super_device, slot);
+ if (metadata) {
+ super_partition_size_bytes = GetTotalSuperPartitionSize(*metadata);
+
+ for (const auto& group : metadata->groups) {
+ if (EndsWith(GetPartitionGroupName(group),
+ fs_mgr_get_slot_suffix(),
+ base::CompareCase::SENSITIVE)) {
+ slot_size_bytes += group.maximum_size;
+ }
+ }
+
+ auto metadata_builder = MetadataBuilder::New(*metadata);
+ if (metadata_builder) {
+ auto free_regions = metadata_builder->GetFreeRegions();
+ for (const auto& interval : free_regions) {
+ super_free_space += interval.length();
+ }
+ super_free_space *= android::dm::kSectorSize;
+ } else {
+ LOG(ERROR) << "Cannot create metadata builder.";
+ }
+ } else {
+ LOG(ERROR) << "Could not read dynamic partition metadata for device: "
+ << super_device;
+ }
+ }
+
+ android::util::stats_write(
+ android::util::UPDATE_ENGINE_UPDATE_ATTEMPT_REPORTED,
+ attempt_number,
+ GetStatsdEnumValue(static_cast<int32_t>(payload_type)),
+ duration.InMinutes(),
+ duration_uptime.InMinutes(),
+ payload_size_mib,
+ GetStatsdEnumValue(static_cast<int32_t>(attempt_result)),
+ GetStatsdEnumValue(static_cast<int32_t>(error_code)),
+ android::base::GetProperty("ro.build.fingerprint", "").c_str(),
+ super_partition_size_bytes,
+ slot_size_bytes,
+ super_free_space);
+}
+
+void MetricsReporterAndroid::ReportUpdateAttemptDownloadMetrics(
+ int64_t payload_bytes_downloaded,
+ int64_t /* payload_download_speed_bps */,
+ DownloadSource /* download_source */,
+ metrics::DownloadErrorCode /* payload_download_error_code */,
+ metrics::ConnectionType /* connection_type */) {
+ // TODO(xunchang) add statsd reporting
+ LOG(INFO) << "Current update attempt downloads "
+ << payload_bytes_downloaded / kNumBytesInOneMiB << " bytes data";
+}
+
+void MetricsReporterAndroid::ReportSuccessfulUpdateMetrics(
+ int attempt_count,
+ int /* updates_abandoned_count */,
+ PayloadType payload_type,
+ int64_t payload_size,
+ int64_t num_bytes_downloaded[kNumDownloadSources],
+ int download_overhead_percentage,
+ base::TimeDelta total_duration,
+ base::TimeDelta /* total_duration_uptime */,
+ int reboot_count,
+ int /* url_switch_count */) {
+ int64_t payload_size_mib = payload_size / kNumBytesInOneMiB;
+ int64_t total_bytes_downloaded = 0;
+ for (size_t i = 0; i < kNumDownloadSources; i++) {
+ total_bytes_downloaded += num_bytes_downloaded[i] / kNumBytesInOneMiB;
+ }
+
+ android::util::stats_write(
+ android::util::UPDATE_ENGINE_SUCCESSFUL_UPDATE_REPORTED,
+ static_cast<int32_t>(attempt_count),
+ GetStatsdEnumValue(static_cast<int32_t>(payload_type)),
+ static_cast<int32_t>(payload_size_mib),
+ static_cast<int32_t>(total_bytes_downloaded),
+ static_cast<int32_t>(download_overhead_percentage),
+ static_cast<int32_t>(total_duration.InMinutes()),
+ static_cast<int32_t>(reboot_count));
+}
+
+void MetricsReporterAndroid::ReportAbnormallyTerminatedUpdateAttemptMetrics() {
+ int attempt_result =
+ static_cast<int>(metrics::AttemptResult::kAbnormalTermination);
+ // TODO(xunchang) add statsd reporting
+ LOG(INFO) << "Abnormally terminated update attempt result " << attempt_result;
+}
+
+}; // namespace chromeos_update_engine
diff --git a/aosp/metrics_reporter_android.h b/aosp/metrics_reporter_android.h
new file mode 100644
index 0000000..4a173bf
--- /dev/null
+++ b/aosp/metrics_reporter_android.h
@@ -0,0 +1,101 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_AOSP_METRICS_REPORTER_ANDROID_H_
+#define UPDATE_ENGINE_AOSP_METRICS_REPORTER_ANDROID_H_
+
+#include <string>
+
+#include "update_engine/common/error_code.h"
+#include "update_engine/common/metrics_constants.h"
+#include "update_engine/common/metrics_reporter_interface.h"
+
+namespace chromeos_update_engine {
+
+class MetricsReporterAndroid : public MetricsReporterInterface {
+ public:
+ MetricsReporterAndroid() = default;
+
+ ~MetricsReporterAndroid() override = default;
+
+ void ReportRollbackMetrics(metrics::RollbackResult result) override {}
+
+ void ReportEnterpriseRollbackMetrics(
+ bool success, const std::string& rollback_version) override {}
+
+ void ReportDailyMetrics(base::TimeDelta os_age) override {}
+
+ void ReportUpdateCheckMetrics(
+ SystemState* system_state,
+ metrics::CheckResult result,
+ metrics::CheckReaction reaction,
+ metrics::DownloadErrorCode download_error_code) override {}
+
+ void ReportUpdateAttemptMetrics(SystemState* system_state,
+ int attempt_number,
+ PayloadType payload_type,
+ base::TimeDelta duration,
+ base::TimeDelta duration_uptime,
+ int64_t payload_size,
+ metrics::AttemptResult attempt_result,
+ ErrorCode internal_error_code) override;
+
+ void ReportUpdateAttemptDownloadMetrics(
+ int64_t payload_bytes_downloaded,
+ int64_t payload_download_speed_bps,
+ DownloadSource download_source,
+ metrics::DownloadErrorCode payload_download_error_code,
+ metrics::ConnectionType connection_type) override;
+
+ void ReportAbnormallyTerminatedUpdateAttemptMetrics() override;
+
+ void ReportSuccessfulUpdateMetrics(
+ int attempt_count,
+ int updates_abandoned_count,
+ PayloadType payload_type,
+ int64_t payload_size,
+ int64_t num_bytes_downloaded[kNumDownloadSources],
+ int download_overhead_percentage,
+ base::TimeDelta total_duration,
+ base::TimeDelta total_duration_uptime,
+ int reboot_count,
+ int url_switch_count) override;
+
+ void ReportCertificateCheckMetrics(ServerToCheck server_to_check,
+ CertificateCheckResult result) override {}
+
+ void ReportFailedUpdateCount(int target_attempt) override {}
+
+ void ReportTimeToReboot(int time_to_reboot_minutes) override {}
+
+ void ReportInstallDateProvisioningSource(int source, int max) override {}
+
+ void ReportInternalErrorCode(ErrorCode error_code) override {}
+
+ void ReportKeyVersionMetrics(int kernel_min_version,
+ int kernel_max_rollforward_version,
+ bool kernel_max_rollforward_success) override {}
+
+ void ReportEnterpriseUpdateSeenToDownloadDays(
+ bool has_time_restriction_policy, int time_to_update_days) override {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MetricsReporterAndroid);
+};
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_AOSP_METRICS_REPORTER_ANDROID_H_
diff --git a/aosp/mock_dynamic_partition_control.h b/aosp/mock_dynamic_partition_control.h
new file mode 100644
index 0000000..df7208e
--- /dev/null
+++ b/aosp/mock_dynamic_partition_control.h
@@ -0,0 +1,129 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include <stdint.h>
+
+#include <memory>
+#include <set>
+#include <string>
+
+#include <gmock/gmock.h>
+
+#include <libsnapshot/cow_writer.h>
+#include <libsnapshot/snapshot_writer.h>
+
+#include "update_engine/aosp/dynamic_partition_control_android.h"
+#include "update_engine/common/boot_control_interface.h"
+#include "update_engine/common/dynamic_partition_control_interface.h"
+
+namespace chromeos_update_engine {
+
+class MockDynamicPartitionControlAndroid
+ : public DynamicPartitionControlAndroid {
+ public:
+ MOCK_METHOD(
+ bool,
+ MapPartitionOnDeviceMapper,
+ (const std::string&, const std::string&, uint32_t, bool, std::string*),
+ (override));
+ MOCK_METHOD(bool,
+ UnmapPartitionOnDeviceMapper,
+ (const std::string&),
+ (override));
+ MOCK_METHOD(void, Cleanup, (), (override));
+ MOCK_METHOD(bool, DeviceExists, (const std::string&), (override));
+ MOCK_METHOD(::android::dm::DmDeviceState,
+ GetState,
+ (const std::string&),
+ (override));
+ MOCK_METHOD(bool,
+ GetDmDevicePathByName,
+ (const std::string&, std::string*),
+ (override));
+ MOCK_METHOD(std::unique_ptr<::android::fs_mgr::MetadataBuilder>,
+ LoadMetadataBuilder,
+ (const std::string&, uint32_t),
+ (override));
+ MOCK_METHOD(std::unique_ptr<::android::fs_mgr::MetadataBuilder>,
+ LoadMetadataBuilder,
+ (const std::string&, uint32_t, uint32_t),
+ (override));
+ MOCK_METHOD(bool,
+ StoreMetadata,
+ (const std::string&, android::fs_mgr::MetadataBuilder*, uint32_t),
+ (override));
+ MOCK_METHOD(bool, GetDeviceDir, (std::string*), (override));
+ MOCK_METHOD(FeatureFlag, GetDynamicPartitionsFeatureFlag, (), (override));
+ MOCK_METHOD(std::string, GetSuperPartitionName, (uint32_t), (override));
+ MOCK_METHOD(FeatureFlag, GetVirtualAbFeatureFlag, (), (override));
+ MOCK_METHOD(bool, FinishUpdate, (bool), (override));
+ MOCK_METHOD(bool,
+ GetSystemOtherPath,
+ (uint32_t, uint32_t, const std::string&, std::string*, bool*),
+ (override));
+ MOCK_METHOD(bool,
+ EraseSystemOtherAvbFooter,
+ (uint32_t, uint32_t),
+ (override));
+ MOCK_METHOD(std::optional<bool>, IsAvbEnabledOnSystemOther, (), (override));
+ MOCK_METHOD(bool, IsRecovery, (), (override));
+ MOCK_METHOD(bool,
+ PrepareDynamicPartitionsForUpdate,
+ (uint32_t, uint32_t, const DeltaArchiveManifest&, bool),
+ (override));
+ MOCK_METHOD(std::unique_ptr<android::snapshot::ISnapshotWriter>,
+ OpenCowWriter,
+ (const std::string& unsuffixed_partition_name,
+ const std::optional<std::string>& source_path,
+ bool is_append),
+ (override));
+ MOCK_METHOD(bool, MapAllPartitions, (), (override));
+ MOCK_METHOD(bool, UnmapAllPartitions, (), (override));
+
+ void set_fake_mapped_devices(const std::set<std::string>& fake) override {
+ DynamicPartitionControlAndroid::set_fake_mapped_devices(fake);
+ }
+
+ bool RealGetSystemOtherPath(uint32_t source_slot,
+ uint32_t target_slot,
+ const std::string& partition_name_suffix,
+ std::string* path,
+ bool* should_unmap) {
+ return DynamicPartitionControlAndroid::GetSystemOtherPath(
+ source_slot, target_slot, partition_name_suffix, path, should_unmap);
+ }
+
+ bool RealEraseSystemOtherAvbFooter(uint32_t source_slot,
+ uint32_t target_slot) {
+ return DynamicPartitionControlAndroid::EraseSystemOtherAvbFooter(
+ source_slot, target_slot);
+ }
+
+ std::optional<bool> RealIsAvbEnabledInFstab(const std::string& path) {
+ return DynamicPartitionControlAndroid::IsAvbEnabledInFstab(path);
+ }
+
+ bool RealPrepareDynamicPartitionsForUpdate(
+ uint32_t source_slot,
+ uint32_t target_slot,
+ const DeltaArchiveManifest& manifest,
+ bool delete_source) {
+ return DynamicPartitionControlAndroid::PrepareDynamicPartitionsForUpdate(
+ source_slot, target_slot, manifest, delete_source);
+ }
+};
+
+} // namespace chromeos_update_engine
diff --git a/aosp/network_selector_android.cc b/aosp/network_selector_android.cc
new file mode 100644
index 0000000..a7db415
--- /dev/null
+++ b/aosp/network_selector_android.cc
@@ -0,0 +1,46 @@
+//
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/aosp/network_selector_android.h"
+
+#include <memory>
+
+#include <android/multinetwork.h>
+#include <base/logging.h>
+
+namespace chromeos_update_engine {
+
+namespace network {
+
+// Factory defined in common/network_selector.h.
+std::unique_ptr<NetworkSelectorInterface> CreateNetworkSelector() {
+ return std::make_unique<NetworkSelectorAndroid>();
+}
+
+} // namespace network
+
+// Defined in common/network_selector_interface.h.
+const NetworkId kDefaultNetworkId = NETWORK_UNSPECIFIED;
+
+bool NetworkSelectorAndroid::SetProcessNetwork(NetworkId network_id) {
+ if (android_setprocnetwork(network_id) < 0) {
+ PLOG(ERROR) << "Binding the network to " << network_id;
+ return false;
+ }
+ return true;
+}
+
+} // namespace chromeos_update_engine
diff --git a/aosp/network_selector_android.h b/aosp/network_selector_android.h
new file mode 100644
index 0000000..b79d1b3
--- /dev/null
+++ b/aosp/network_selector_android.h
@@ -0,0 +1,40 @@
+//
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_AOSP_NETWORK_SELECTOR_ANDROID_H_
+#define UPDATE_ENGINE_AOSP_NETWORK_SELECTOR_ANDROID_H_
+
+#include <base/macros.h>
+
+#include "update_engine/common/network_selector_interface.h"
+
+namespace chromeos_update_engine {
+
+class NetworkSelectorAndroid final : public NetworkSelectorInterface {
+ public:
+ NetworkSelectorAndroid() = default;
+ ~NetworkSelectorAndroid() override = default;
+
+ // NetworkSelectorInterface overrides.
+ bool SetProcessNetwork(NetworkId network_id) override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NetworkSelectorAndroid);
+};
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_AOSP_NETWORK_SELECTOR_ANDROID_H_
diff --git a/aosp/platform_constants_android.cc b/aosp/platform_constants_android.cc
new file mode 100644
index 0000000..f468c3b
--- /dev/null
+++ b/aosp/platform_constants_android.cc
@@ -0,0 +1,38 @@
+//
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/common/platform_constants.h"
+
+namespace chromeos_update_engine {
+namespace constants {
+
+const char kOmahaDefaultProductionURL[] =
+ "https://clients2.google.com/service/update2/brillo";
+const char kOmahaDefaultAUTestURL[] =
+ "https://clients2.google.com/service/update2/brillo";
+const char kOmahaUpdaterID[] = "Brillo";
+const char kOmahaPlatformName[] = "Brillo";
+const char kUpdatePayloadPublicKeyPath[] = "";
+const char kUpdateCertificatesPath[] = "/system/etc/security/otacerts.zip";
+const char kCACertificatesPath[] = "/system/etc/security/cacerts_google";
+// No deadline file API support on Android.
+const char kOmahaResponseDeadlineFile[] = "";
+const char kNonVolatileDirectory[] = "/data/misc/update_engine";
+const char kPostinstallMountOptions[] =
+ "context=u:object_r:postinstall_file:s0";
+
+} // namespace constants
+} // namespace chromeos_update_engine
diff --git a/aosp/service_delegate_android_interface.h b/aosp/service_delegate_android_interface.h
new file mode 100644
index 0000000..3c28794
--- /dev/null
+++ b/aosp/service_delegate_android_interface.h
@@ -0,0 +1,127 @@
+//
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_AOSP_SERVICE_DELEGATE_ANDROID_INTERFACE_H_
+#define UPDATE_ENGINE_AOSP_SERVICE_DELEGATE_ANDROID_INTERFACE_H_
+
+#include <inttypes.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <brillo/errors/error.h>
+
+namespace chromeos_update_engine {
+
+// See ServiceDelegateAndroidInterface.CleanupSuccessfulUpdate
+// Wraps a IUpdateEngineCallback binder object used specifically for
+// CleanupSuccessfulUpdate.
+class CleanupSuccessfulUpdateCallbackInterface {
+ public:
+ virtual ~CleanupSuccessfulUpdateCallbackInterface() {}
+ virtual void OnCleanupProgressUpdate(double progress) = 0;
+ virtual void OnCleanupComplete(int32_t error_code) = 0;
+ // Call RegisterForDeathNotifications on the internal binder object.
+ virtual void RegisterForDeathNotifications(base::Closure unbind) = 0;
+};
+
+// This class defines the interface exposed by the Android version of the
+// daemon service. This interface only includes the method calls that such
+// daemon exposes. For asynchronous events initiated by a class implementing
+// this interface see the ServiceObserverInterface class.
+class ServiceDelegateAndroidInterface {
+ public:
+ virtual ~ServiceDelegateAndroidInterface() = default;
+
+ // Start an update attempt to download an apply the provided |payload_url| if
+ // no other update is running. The extra |key_value_pair_headers| will be
+ // included when fetching the payload. Returns whether the update was started
+ // successfully, which means that no other update was running and the passed
+ // parameters were correct, but not necessarily that the update finished
+ // correctly.
+ virtual bool ApplyPayload(
+ const std::string& payload_url,
+ int64_t payload_offset,
+ int64_t payload_size,
+ const std::vector<std::string>& key_value_pair_headers,
+ brillo::ErrorPtr* error) = 0;
+
+ virtual bool ApplyPayload(
+ int fd,
+ int64_t payload_offset,
+ int64_t payload_size,
+ const std::vector<std::string>& key_value_pair_headers,
+ brillo::ErrorPtr* error) = 0;
+
+ // Suspend an ongoing update. Returns true if there was an update ongoing and
+ // it was suspended. In case of failure, it returns false and sets |error|
+ // accordingly.
+ virtual bool SuspendUpdate(brillo::ErrorPtr* error) = 0;
+
+ // Resumes an update suspended with SuspendUpdate(). The update can't be
+ // suspended after it finished and this method will fail in that case.
+ // Returns whether the resume operation was successful, which only implies
+ // that there was a suspended update. In case of error, returns false and sets
+ // |error| accordingly.
+ virtual bool ResumeUpdate(brillo::ErrorPtr* error) = 0;
+
+ // Cancel the ongoing update. The update could be running or suspended, but it
+ // can't be canceled after it was done. In case of error, returns false and
+ // sets |error| accordingly.
+ virtual bool CancelUpdate(brillo::ErrorPtr* error) = 0;
+
+ // Reset the already applied update back to an idle state. This method can
+ // only be called when no update attempt is going on, and it will reset the
+ // status back to idle, deleting the currently applied update if any. In case
+ // of error, returns false and sets |error| accordingly.
+ virtual bool ResetStatus(brillo::ErrorPtr* error) = 0;
+
+ // Verifies whether a payload (delegated by the payload metadata) can be
+ // applied to the current device. Returns whether the payload is applicable.
+ // In case of error, returns false and sets |error| accordingly.
+ virtual bool VerifyPayloadApplicable(const std::string& metadata_filename,
+ brillo::ErrorPtr* error) = 0;
+
+ // Allocates space for a payload.
+ // Returns 0 if space is successfully preallocated.
+ // Return non-zero if not enough space is not available; returned value is
+ // the total space required (in bytes) to be free on the device for this
+ // update to be applied, and |error| is unset.
+ // In case of error, returns 0, and sets |error| accordingly.
+ //
+ // This function may block for several minutes in the worst case.
+ virtual uint64_t AllocateSpaceForPayload(
+ const std::string& metadata_filename,
+ const std::vector<std::string>& key_value_pair_headers,
+ brillo::ErrorPtr* error) = 0;
+
+ // Wait for merge to complete, then clean up merge after an update has been
+ // successful.
+ //
+ // This function returns immediately. Progress updates are provided in
+ // |callback|.
+ virtual void CleanupSuccessfulUpdate(
+ std::unique_ptr<CleanupSuccessfulUpdateCallbackInterface> callback,
+ brillo::ErrorPtr* error) = 0;
+
+ protected:
+ ServiceDelegateAndroidInterface() = default;
+};
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_AOSP_SERVICE_DELEGATE_ANDROID_INTERFACE_H_
diff --git a/aosp/sideload_main.cc b/aosp/sideload_main.cc
new file mode 100644
index 0000000..3cbc0c7
--- /dev/null
+++ b/aosp/sideload_main.cc
@@ -0,0 +1,203 @@
+//
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include <xz.h>
+
+#include <string>
+#include <vector>
+
+#include <base/command_line.h>
+#include <base/strings/string_split.h>
+#include <base/strings/stringprintf.h>
+#include <brillo/asynchronous_signal_handler.h>
+#include <brillo/flag_helper.h>
+#include <brillo/message_loops/base_message_loop.h>
+#include <brillo/streams/file_stream.h>
+#include <brillo/streams/stream.h>
+
+#include "update_engine/aosp/update_attempter_android.h"
+#include "update_engine/common/boot_control.h"
+#include "update_engine/common/error_code_utils.h"
+#include "update_engine/common/hardware.h"
+#include "update_engine/common/logging.h"
+#include "update_engine/common/prefs.h"
+#include "update_engine/common/subprocess.h"
+#include "update_engine/common/terminator.h"
+#include "update_engine/common/utils.h"
+
+using std::string;
+using std::vector;
+using update_engine::UpdateEngineStatus;
+using update_engine::UpdateStatus;
+
+namespace chromeos_update_engine {
+namespace {
+
+class SideloadDaemonState : public DaemonStateInterface,
+ public ServiceObserverInterface {
+ public:
+ explicit SideloadDaemonState(brillo::StreamPtr status_stream)
+ : status_stream_(std::move(status_stream)) {
+ // Add this class as the only observer.
+ observers_.insert(this);
+ }
+ ~SideloadDaemonState() override = default;
+
+ // DaemonStateInterface overrides.
+ bool StartUpdater() override { return true; }
+ void AddObserver(ServiceObserverInterface* observer) override {}
+ void RemoveObserver(ServiceObserverInterface* observer) override {}
+ const std::set<ServiceObserverInterface*>& service_observers() override {
+ return observers_;
+ }
+
+ // ServiceObserverInterface overrides.
+ void SendStatusUpdate(
+ const UpdateEngineStatus& update_engine_status) override {
+ UpdateStatus status = update_engine_status.status;
+ double progress = update_engine_status.progress;
+ if (status_ != status && (status == UpdateStatus::DOWNLOADING ||
+ status == UpdateStatus::FINALIZING)) {
+ // Split the progress bar in two parts for the two stages DOWNLOADING and
+ // FINALIZING.
+ ReportStatus(base::StringPrintf(
+ "ui_print Step %d/2", status == UpdateStatus::DOWNLOADING ? 1 : 2));
+ ReportStatus(base::StringPrintf("progress 0.5 0"));
+ }
+ if (status_ != status || fabs(progress - progress_) > 0.005) {
+ ReportStatus(base::StringPrintf("set_progress %.lf", progress));
+ }
+ progress_ = progress;
+ status_ = status;
+ }
+
+ void SendPayloadApplicationComplete(ErrorCode error_code) override {
+ if (error_code != ErrorCode::kSuccess) {
+ ReportStatus(
+ base::StringPrintf("ui_print Error applying update: %d (%s)",
+ error_code,
+ utils::ErrorCodeToString(error_code).c_str()));
+ }
+ error_code_ = error_code;
+ brillo::MessageLoop::current()->BreakLoop();
+ }
+
+ // Getters.
+ UpdateStatus status() { return status_; }
+ ErrorCode error_code() { return error_code_; }
+
+ private:
+ // Report a status message in the status_stream_, if any. These messages
+ // should conform to the specification defined in the Android recovery.
+ void ReportStatus(const string& message) {
+ if (!status_stream_)
+ return;
+ string status_line = message + "\n";
+ status_stream_->WriteAllBlocking(
+ status_line.data(), status_line.size(), nullptr);
+ }
+
+ std::set<ServiceObserverInterface*> observers_;
+ brillo::StreamPtr status_stream_;
+
+ // The last status and error code reported.
+ UpdateStatus status_{UpdateStatus::IDLE};
+ ErrorCode error_code_{ErrorCode::kSuccess};
+ double progress_{-1.};
+};
+
+// Apply an update payload directly from the given payload URI.
+bool ApplyUpdatePayload(const string& payload,
+ int64_t payload_offset,
+ int64_t payload_size,
+ const vector<string>& headers,
+ int64_t status_fd) {
+ brillo::BaseMessageLoop loop;
+ loop.SetAsCurrent();
+
+ // Setup the subprocess handler.
+ brillo::AsynchronousSignalHandler handler;
+ handler.Init();
+ Subprocess subprocess;
+ subprocess.Init(&handler);
+
+ SideloadDaemonState sideload_daemon_state(
+ brillo::FileStream::FromFileDescriptor(status_fd, true, nullptr));
+
+ // During the sideload we don't access the prefs persisted on disk but instead
+ // use a temporary memory storage.
+ MemoryPrefs prefs;
+
+ std::unique_ptr<BootControlInterface> boot_control =
+ boot_control::CreateBootControl();
+ if (!boot_control) {
+ LOG(ERROR) << "Error initializing the BootControlInterface.";
+ return false;
+ }
+
+ std::unique_ptr<HardwareInterface> hardware = hardware::CreateHardware();
+ if (!hardware) {
+ LOG(ERROR) << "Error initializing the HardwareInterface.";
+ return false;
+ }
+
+ UpdateAttempterAndroid update_attempter(
+ &sideload_daemon_state, &prefs, boot_control.get(), hardware.get());
+ update_attempter.Init();
+
+ TEST_AND_RETURN_FALSE(update_attempter.ApplyPayload(
+ payload, payload_offset, payload_size, headers, nullptr));
+
+ loop.Run();
+ return sideload_daemon_state.status() == UpdateStatus::UPDATED_NEED_REBOOT;
+}
+
+} // namespace
+} // namespace chromeos_update_engine
+
+int main(int argc, char** argv) {
+ DEFINE_string(payload,
+ "file:///data/payload.bin",
+ "The URI to the update payload to use.");
+ DEFINE_int64(
+ offset, 0, "The offset in the payload where the CrAU update starts. ");
+ DEFINE_int64(size,
+ 0,
+ "The size of the CrAU part of the payload. If 0 is passed, it "
+ "will be autodetected.");
+ DEFINE_string(headers,
+ "",
+ "A list of key-value pairs, one element of the list per line.");
+ DEFINE_int64(status_fd, -1, "A file descriptor to notify the update status.");
+
+ chromeos_update_engine::Terminator::Init();
+ chromeos_update_engine::SetupLogging(true /* stderr */, false /* file */);
+ brillo::FlagHelper::Init(argc, argv, "Update Engine Sideload");
+
+ LOG(INFO) << "Update Engine Sideloading starting";
+
+ // xz-embedded requires to initialize its CRC-32 table once on startup.
+ xz_crc32_init();
+
+ vector<string> headers = base::SplitString(
+ FLAGS_headers, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+
+ if (!chromeos_update_engine::ApplyUpdatePayload(
+ FLAGS_payload, FLAGS_offset, FLAGS_size, headers, FLAGS_status_fd))
+ return 1;
+
+ return 0;
+}
diff --git a/aosp/update_attempter_android.cc b/aosp/update_attempter_android.cc
new file mode 100644
index 0000000..7ed3249
--- /dev/null
+++ b/aosp/update_attempter_android.cc
@@ -0,0 +1,1054 @@
+//
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/aosp/update_attempter_android.h"
+
+#include <algorithm>
+#include <map>
+#include <memory>
+#include <utility>
+
+#include <android-base/properties.h>
+#include <android-base/unique_fd.h>
+#include <base/bind.h>
+#include <base/logging.h>
+#include <base/strings/string_number_conversions.h>
+#include <brillo/data_encoding.h>
+#include <brillo/message_loops/message_loop.h>
+#include <brillo/strings/string_utils.h>
+#include <log/log_safetynet.h>
+
+#include "update_engine/aosp/cleanup_previous_update_action.h"
+#include "update_engine/common/constants.h"
+#include "update_engine/common/daemon_state_interface.h"
+#include "update_engine/common/download_action.h"
+#include "update_engine/common/error_code_utils.h"
+#include "update_engine/common/file_fetcher.h"
+#include "update_engine/common/metrics_reporter_interface.h"
+#include "update_engine/common/network_selector.h"
+#include "update_engine/common/utils.h"
+#include "update_engine/metrics_utils.h"
+#include "update_engine/payload_consumer/certificate_parser_interface.h"
+#include "update_engine/payload_consumer/delta_performer.h"
+#include "update_engine/payload_consumer/file_descriptor.h"
+#include "update_engine/payload_consumer/file_descriptor_utils.h"
+#include "update_engine/payload_consumer/filesystem_verifier_action.h"
+#include "update_engine/payload_consumer/payload_constants.h"
+#include "update_engine/payload_consumer/payload_metadata.h"
+#include "update_engine/payload_consumer/payload_verifier.h"
+#include "update_engine/payload_consumer/postinstall_runner_action.h"
+#include "update_engine/update_boot_flags_action.h"
+#include "update_engine/update_status_utils.h"
+
+#ifndef _UE_SIDELOAD
+// Do not include support for external HTTP(s) urls when building
+// update_engine_sideload.
+#include "update_engine/libcurl_http_fetcher.h"
+#endif
+
+using android::base::unique_fd;
+using base::Bind;
+using base::Time;
+using base::TimeDelta;
+using base::TimeTicks;
+using std::shared_ptr;
+using std::string;
+using std::vector;
+using update_engine::UpdateEngineStatus;
+
+namespace chromeos_update_engine {
+
+namespace {
+
+// Minimum threshold to broadcast an status update in progress and time.
+const double kBroadcastThresholdProgress = 0.01; // 1%
+const int kBroadcastThresholdSeconds = 10;
+
+const char* const kErrorDomain = "update_engine";
+// TODO(deymo): Convert the different errors to a numeric value to report them
+// back on the service error.
+const char* const kGenericError = "generic_error";
+
+// Log and set the error on the passed ErrorPtr.
+bool LogAndSetError(brillo::ErrorPtr* error,
+ const base::Location& location,
+ const string& reason) {
+ brillo::Error::AddTo(error, location, kErrorDomain, kGenericError, reason);
+ LOG(ERROR) << "Replying with failure: " << location.ToString() << ": "
+ << reason;
+ return false;
+}
+
+bool GetHeaderAsBool(const string& header, bool default_value) {
+ int value = 0;
+ if (base::StringToInt(header, &value) && (value == 0 || value == 1))
+ return value == 1;
+ return default_value;
+}
+
+bool ParseKeyValuePairHeaders(const vector<string>& key_value_pair_headers,
+ std::map<string, string>* headers,
+ brillo::ErrorPtr* error) {
+ for (const string& key_value_pair : key_value_pair_headers) {
+ string key;
+ string value;
+ if (!brillo::string_utils::SplitAtFirst(
+ key_value_pair, "=", &key, &value, false)) {
+ return LogAndSetError(
+ error, FROM_HERE, "Passed invalid header: " + key_value_pair);
+ }
+ if (!headers->emplace(key, value).second)
+ return LogAndSetError(error, FROM_HERE, "Passed repeated key: " + key);
+ }
+ return true;
+}
+
+// Unique identifier for the payload. An empty string means that the payload
+// can't be resumed.
+string GetPayloadId(const std::map<string, string>& headers) {
+ return (headers.count(kPayloadPropertyFileHash)
+ ? headers.at(kPayloadPropertyFileHash)
+ : "") +
+ (headers.count(kPayloadPropertyMetadataHash)
+ ? headers.at(kPayloadPropertyMetadataHash)
+ : "");
+}
+
+} // namespace
+
+UpdateAttempterAndroid::UpdateAttempterAndroid(
+ DaemonStateInterface* daemon_state,
+ PrefsInterface* prefs,
+ BootControlInterface* boot_control,
+ HardwareInterface* hardware)
+ : daemon_state_(daemon_state),
+ prefs_(prefs),
+ boot_control_(boot_control),
+ hardware_(hardware),
+ processor_(new ActionProcessor()),
+ clock_(new Clock()) {
+ metrics_reporter_ = metrics::CreateMetricsReporter();
+ network_selector_ = network::CreateNetworkSelector();
+}
+
+UpdateAttempterAndroid::~UpdateAttempterAndroid() {
+ // Release ourselves as the ActionProcessor's delegate to prevent
+ // re-scheduling the updates due to the processing stopped.
+ processor_->set_delegate(nullptr);
+}
+
+void UpdateAttempterAndroid::Init() {
+ // In case of update_engine restart without a reboot we need to restore the
+ // reboot needed state.
+ if (UpdateCompletedOnThisBoot()) {
+ SetStatusAndNotify(UpdateStatus::UPDATED_NEED_REBOOT);
+ } else {
+ SetStatusAndNotify(UpdateStatus::IDLE);
+ UpdatePrefsAndReportUpdateMetricsOnReboot();
+#ifdef _UE_SIDELOAD
+ LOG(INFO) << "Skip ScheduleCleanupPreviousUpdate in sideload because "
+ << "ApplyPayload will call it later.";
+#else
+ ScheduleCleanupPreviousUpdate();
+#endif
+ }
+}
+
+bool UpdateAttempterAndroid::ApplyPayload(
+ const string& payload_url,
+ int64_t payload_offset,
+ int64_t payload_size,
+ const vector<string>& key_value_pair_headers,
+ brillo::ErrorPtr* error) {
+ if (status_ == UpdateStatus::UPDATED_NEED_REBOOT) {
+ return LogAndSetError(
+ error, FROM_HERE, "An update already applied, waiting for reboot");
+ }
+ if (processor_->IsRunning()) {
+ return LogAndSetError(
+ error, FROM_HERE, "Already processing an update, cancel it first.");
+ }
+ DCHECK(status_ == UpdateStatus::IDLE);
+
+ std::map<string, string> headers;
+ if (!ParseKeyValuePairHeaders(key_value_pair_headers, &headers, error)) {
+ return false;
+ }
+
+ string payload_id = GetPayloadId(headers);
+
+ // Setup the InstallPlan based on the request.
+ install_plan_ = InstallPlan();
+
+ install_plan_.download_url = payload_url;
+ install_plan_.version = "";
+ base_offset_ = payload_offset;
+ InstallPlan::Payload payload;
+ payload.size = payload_size;
+ if (!payload.size) {
+ if (!base::StringToUint64(headers[kPayloadPropertyFileSize],
+ &payload.size)) {
+ payload.size = 0;
+ }
+ }
+ if (!brillo::data_encoding::Base64Decode(headers[kPayloadPropertyFileHash],
+ &payload.hash)) {
+ LOG(WARNING) << "Unable to decode base64 file hash: "
+ << headers[kPayloadPropertyFileHash];
+ }
+ if (!base::StringToUint64(headers[kPayloadPropertyMetadataSize],
+ &payload.metadata_size)) {
+ payload.metadata_size = 0;
+ }
+ // The |payload.type| is not used anymore since minor_version 3.
+ payload.type = InstallPayloadType::kUnknown;
+ install_plan_.payloads.push_back(payload);
+
+ // The |public_key_rsa| key would override the public key stored on disk.
+ install_plan_.public_key_rsa = "";
+
+ install_plan_.hash_checks_mandatory = hardware_->IsOfficialBuild();
+ install_plan_.is_resume = !payload_id.empty() &&
+ DeltaPerformer::CanResumeUpdate(prefs_, payload_id);
+ if (!install_plan_.is_resume) {
+ // No need to reset dynamic_partititon_metadata_updated. If previous calls
+ // to AllocateSpaceForPayload uses the same payload_id, reuse preallocated
+ // space. Otherwise, DeltaPerformer re-allocates space when the payload is
+ // applied.
+ if (!DeltaPerformer::ResetUpdateProgress(
+ prefs_,
+ false /* quick */,
+ true /* skip_dynamic_partititon_metadata_updated */)) {
+ LOG(WARNING) << "Unable to reset the update progress.";
+ }
+ if (!prefs_->SetString(kPrefsUpdateCheckResponseHash, payload_id)) {
+ LOG(WARNING) << "Unable to save the update check response hash.";
+ }
+ }
+ install_plan_.source_slot = GetCurrentSlot();
+ install_plan_.target_slot = GetTargetSlot();
+
+ install_plan_.powerwash_required =
+ GetHeaderAsBool(headers[kPayloadPropertyPowerwash], false);
+
+ install_plan_.switch_slot_on_reboot =
+ GetHeaderAsBool(headers[kPayloadPropertySwitchSlotOnReboot], true);
+
+ install_plan_.run_post_install =
+ GetHeaderAsBool(headers[kPayloadPropertyRunPostInstall], true);
+
+ // Skip writing verity if we're resuming and verity has already been written.
+ install_plan_.write_verity = true;
+ if (install_plan_.is_resume && prefs_->Exists(kPrefsVerityWritten)) {
+ bool verity_written = false;
+ if (prefs_->GetBoolean(kPrefsVerityWritten, &verity_written) &&
+ verity_written) {
+ install_plan_.write_verity = false;
+ }
+ }
+
+ NetworkId network_id = kDefaultNetworkId;
+ if (!headers[kPayloadPropertyNetworkId].empty()) {
+ if (!base::StringToUint64(headers[kPayloadPropertyNetworkId],
+ &network_id)) {
+ return LogAndSetError(
+ error,
+ FROM_HERE,
+ "Invalid network_id: " + headers[kPayloadPropertyNetworkId]);
+ }
+ if (!network_selector_->SetProcessNetwork(network_id)) {
+ return LogAndSetError(
+ error,
+ FROM_HERE,
+ "Unable to set network_id: " + headers[kPayloadPropertyNetworkId]);
+ }
+ }
+
+ LOG(INFO) << "Using this install plan:";
+ install_plan_.Dump();
+
+ HttpFetcher* fetcher = nullptr;
+ if (FileFetcher::SupportedUrl(payload_url)) {
+ DLOG(INFO) << "Using FileFetcher for file URL.";
+ fetcher = new FileFetcher();
+ } else {
+#ifdef _UE_SIDELOAD
+ LOG(FATAL) << "Unsupported sideload URI: " << payload_url;
+#else
+ LibcurlHttpFetcher* libcurl_fetcher =
+ new LibcurlHttpFetcher(&proxy_resolver_, hardware_);
+ libcurl_fetcher->set_server_to_check(ServerToCheck::kDownload);
+ fetcher = libcurl_fetcher;
+#endif // _UE_SIDELOAD
+ }
+ // Setup extra headers.
+ if (!headers[kPayloadPropertyAuthorization].empty())
+ fetcher->SetHeader("Authorization", headers[kPayloadPropertyAuthorization]);
+ if (!headers[kPayloadPropertyUserAgent].empty())
+ fetcher->SetHeader("User-Agent", headers[kPayloadPropertyUserAgent]);
+
+ BuildUpdateActions(fetcher);
+
+ SetStatusAndNotify(UpdateStatus::UPDATE_AVAILABLE);
+
+ UpdatePrefsOnUpdateStart(install_plan_.is_resume);
+ // TODO(xunchang) report the metrics for unresumable updates
+
+ ScheduleProcessingStart();
+ return true;
+}
+
+bool UpdateAttempterAndroid::ApplyPayload(
+ int fd,
+ int64_t payload_offset,
+ int64_t payload_size,
+ const vector<string>& key_value_pair_headers,
+ brillo::ErrorPtr* error) {
+ payload_fd_.reset(dup(fd));
+ const string payload_url = "fd://" + std::to_string(payload_fd_.get());
+
+ return ApplyPayload(
+ payload_url, payload_offset, payload_size, key_value_pair_headers, error);
+}
+
+bool UpdateAttempterAndroid::SuspendUpdate(brillo::ErrorPtr* error) {
+ if (!processor_->IsRunning())
+ return LogAndSetError(error, FROM_HERE, "No ongoing update to suspend.");
+ processor_->SuspendProcessing();
+ return true;
+}
+
+bool UpdateAttempterAndroid::ResumeUpdate(brillo::ErrorPtr* error) {
+ if (!processor_->IsRunning())
+ return LogAndSetError(error, FROM_HERE, "No ongoing update to resume.");
+ processor_->ResumeProcessing();
+ return true;
+}
+
+bool UpdateAttempterAndroid::CancelUpdate(brillo::ErrorPtr* error) {
+ if (!processor_->IsRunning())
+ return LogAndSetError(error, FROM_HERE, "No ongoing update to cancel.");
+ processor_->StopProcessing();
+ return true;
+}
+
+bool UpdateAttempterAndroid::ResetStatus(brillo::ErrorPtr* error) {
+ LOG(INFO) << "Attempting to reset state from "
+ << UpdateStatusToString(status_) << " to UpdateStatus::IDLE";
+
+ switch (status_) {
+ case UpdateStatus::IDLE: {
+ if (!boot_control_->GetDynamicPartitionControl()->ResetUpdate(prefs_)) {
+ LOG(WARNING) << "Failed to reset snapshots. UpdateStatus is IDLE but"
+ << "space might not be freed.";
+ }
+ return true;
+ }
+
+ case UpdateStatus::UPDATED_NEED_REBOOT: {
+ bool ret_value = true;
+
+ // Update the boot flags so the current slot has higher priority.
+ if (!boot_control_->SetActiveBootSlot(GetCurrentSlot()))
+ ret_value = false;
+
+ // Mark the current slot as successful again, since marking it as active
+ // may reset the successful bit. We ignore the result of whether marking
+ // the current slot as successful worked.
+ if (!boot_control_->MarkBootSuccessfulAsync(Bind([](bool successful) {})))
+ ret_value = false;
+
+ // Resets the warm reset property since we won't switch the slot.
+ hardware_->SetWarmReset(false);
+
+ // Remove update progress for DeltaPerformer and remove snapshots.
+ if (!boot_control_->GetDynamicPartitionControl()->ResetUpdate(prefs_))
+ ret_value = false;
+
+ // Remove the reboot marker so that if the machine is rebooted
+ // after resetting to idle state, it doesn't go back to
+ // UpdateStatus::UPDATED_NEED_REBOOT state.
+ if (!prefs_->Delete(kPrefsUpdateCompletedOnBootId))
+ ret_value = false;
+ ClearMetricsPrefs();
+
+ if (!ret_value) {
+ return LogAndSetError(
+ error, FROM_HERE, "Failed to reset the status to ");
+ }
+
+ SetStatusAndNotify(UpdateStatus::IDLE);
+ LOG(INFO) << "Reset status successful";
+ return true;
+ }
+
+ default:
+ return LogAndSetError(
+ error,
+ FROM_HERE,
+ "Reset not allowed in this state. Cancel the ongoing update first");
+ }
+}
+
+bool UpdateAttempterAndroid::VerifyPayloadParseManifest(
+ const std::string& metadata_filename,
+ DeltaArchiveManifest* manifest,
+ brillo::ErrorPtr* error) {
+ FileDescriptorPtr fd(new EintrSafeFileDescriptor);
+ if (!fd->Open(metadata_filename.c_str(), O_RDONLY)) {
+ return LogAndSetError(
+ error, FROM_HERE, "Failed to open " + metadata_filename);
+ }
+ brillo::Blob metadata(kMaxPayloadHeaderSize);
+ if (!fd->Read(metadata.data(), metadata.size())) {
+ return LogAndSetError(
+ error,
+ FROM_HERE,
+ "Failed to read payload header from " + metadata_filename);
+ }
+ ErrorCode errorcode;
+ PayloadMetadata payload_metadata;
+ if (payload_metadata.ParsePayloadHeader(metadata, &errorcode) !=
+ MetadataParseResult::kSuccess) {
+ return LogAndSetError(error,
+ FROM_HERE,
+ "Failed to parse payload header: " +
+ utils::ErrorCodeToString(errorcode));
+ }
+ uint64_t metadata_size = payload_metadata.GetMetadataSize() +
+ payload_metadata.GetMetadataSignatureSize();
+ if (metadata_size < kMaxPayloadHeaderSize ||
+ metadata_size >
+ static_cast<uint64_t>(utils::FileSize(metadata_filename))) {
+ return LogAndSetError(
+ error,
+ FROM_HERE,
+ "Invalid metadata size: " + std::to_string(metadata_size));
+ }
+ metadata.resize(metadata_size);
+ if (!fd->Read(metadata.data() + kMaxPayloadHeaderSize,
+ metadata.size() - kMaxPayloadHeaderSize)) {
+ return LogAndSetError(
+ error,
+ FROM_HERE,
+ "Failed to read metadata and signature from " + metadata_filename);
+ }
+ fd->Close();
+
+ auto payload_verifier = PayloadVerifier::CreateInstanceFromZipPath(
+ constants::kUpdateCertificatesPath);
+ if (!payload_verifier) {
+ return LogAndSetError(error,
+ FROM_HERE,
+ "Failed to create the payload verifier from " +
+ std::string(constants::kUpdateCertificatesPath));
+ }
+ errorcode = payload_metadata.ValidateMetadataSignature(
+ metadata, "", *payload_verifier);
+ if (errorcode != ErrorCode::kSuccess) {
+ return LogAndSetError(error,
+ FROM_HERE,
+ "Failed to validate metadata signature: " +
+ utils::ErrorCodeToString(errorcode));
+ }
+ if (!payload_metadata.GetManifest(metadata, manifest)) {
+ return LogAndSetError(error, FROM_HERE, "Failed to parse manifest.");
+ }
+
+ return true;
+}
+
+bool UpdateAttempterAndroid::VerifyPayloadApplicable(
+ const std::string& metadata_filename, brillo::ErrorPtr* error) {
+ DeltaArchiveManifest manifest;
+ TEST_AND_RETURN_FALSE(
+ VerifyPayloadParseManifest(metadata_filename, &manifest, error));
+
+ FileDescriptorPtr fd(new EintrSafeFileDescriptor);
+ ErrorCode errorcode;
+
+ BootControlInterface::Slot current_slot = GetCurrentSlot();
+ for (const PartitionUpdate& partition : manifest.partitions()) {
+ if (!partition.has_old_partition_info())
+ continue;
+ string partition_path;
+ if (!boot_control_->GetPartitionDevice(
+ partition.partition_name(), current_slot, &partition_path)) {
+ return LogAndSetError(
+ error,
+ FROM_HERE,
+ "Failed to get partition device for " + partition.partition_name());
+ }
+ if (!fd->Open(partition_path.c_str(), O_RDONLY)) {
+ return LogAndSetError(
+ error, FROM_HERE, "Failed to open " + partition_path);
+ }
+ for (const InstallOperation& operation : partition.operations()) {
+ if (!operation.has_src_sha256_hash())
+ continue;
+ brillo::Blob source_hash;
+ if (!fd_utils::ReadAndHashExtents(fd,
+ operation.src_extents(),
+ manifest.block_size(),
+ &source_hash)) {
+ return LogAndSetError(
+ error, FROM_HERE, "Failed to hash " + partition_path);
+ }
+ if (!PartitionWriter::ValidateSourceHash(
+ source_hash, operation, fd, &errorcode)) {
+ return false;
+ }
+ }
+ fd->Close();
+ }
+ return true;
+}
+
+void UpdateAttempterAndroid::ProcessingDone(const ActionProcessor* processor,
+ ErrorCode code) {
+ LOG(INFO) << "Processing Done.";
+
+ if (status_ == UpdateStatus::CLEANUP_PREVIOUS_UPDATE) {
+ TerminateUpdateAndNotify(code);
+ return;
+ }
+
+ switch (code) {
+ case ErrorCode::kSuccess:
+ // Update succeeded.
+ WriteUpdateCompletedMarker();
+ prefs_->SetInt64(kPrefsDeltaUpdateFailures, 0);
+
+ LOG(INFO) << "Update successfully applied, waiting to reboot.";
+ break;
+
+ case ErrorCode::kFilesystemCopierError:
+ case ErrorCode::kNewRootfsVerificationError:
+ case ErrorCode::kNewKernelVerificationError:
+ case ErrorCode::kFilesystemVerifierError:
+ case ErrorCode::kDownloadStateInitializationError:
+ // Reset the ongoing update for these errors so it starts from the
+ // beginning next time.
+ DeltaPerformer::ResetUpdateProgress(prefs_, false);
+ LOG(INFO) << "Resetting update progress.";
+ break;
+
+ case ErrorCode::kPayloadTimestampError:
+ // SafetyNet logging, b/36232423
+ android_errorWriteLog(0x534e4554, "36232423");
+ break;
+
+ default:
+ // Ignore all other error codes.
+ break;
+ }
+
+ TerminateUpdateAndNotify(code);
+}
+
+void UpdateAttempterAndroid::ProcessingStopped(
+ const ActionProcessor* processor) {
+ TerminateUpdateAndNotify(ErrorCode::kUserCanceled);
+}
+
+void UpdateAttempterAndroid::ActionCompleted(ActionProcessor* processor,
+ AbstractAction* action,
+ ErrorCode code) {
+ // Reset download progress regardless of whether or not the download
+ // action succeeded.
+ const string type = action->Type();
+ if (type == CleanupPreviousUpdateAction::StaticType() ||
+ (type == NoOpAction::StaticType() &&
+ status_ == UpdateStatus::CLEANUP_PREVIOUS_UPDATE)) {
+ cleanup_previous_update_code_ = code;
+ NotifyCleanupPreviousUpdateCallbacksAndClear();
+ }
+ // download_progress_ is actually used by other actions, such as
+ // filesystem_verify_action. Therefore we always clear it.
+ download_progress_ = 0;
+ if (type == PostinstallRunnerAction::StaticType()) {
+ bool succeeded =
+ code == ErrorCode::kSuccess || code == ErrorCode::kUpdatedButNotActive;
+ prefs_->SetBoolean(kPrefsPostInstallSucceeded, succeeded);
+ }
+ if (code != ErrorCode::kSuccess) {
+ // If an action failed, the ActionProcessor will cancel the whole thing.
+ return;
+ }
+ if (type == UpdateBootFlagsAction::StaticType()) {
+ SetStatusAndNotify(UpdateStatus::CLEANUP_PREVIOUS_UPDATE);
+ }
+ if (type == DownloadAction::StaticType()) {
+ auto download_action = static_cast<DownloadAction*>(action);
+ install_plan_ = *download_action->install_plan();
+ SetStatusAndNotify(UpdateStatus::VERIFYING);
+ } else if (type == FilesystemVerifierAction::StaticType()) {
+ SetStatusAndNotify(UpdateStatus::FINALIZING);
+ prefs_->SetBoolean(kPrefsVerityWritten, true);
+ }
+}
+
+void UpdateAttempterAndroid::BytesReceived(uint64_t bytes_progressed,
+ uint64_t bytes_received,
+ uint64_t total) {
+ double progress = 0;
+ if (total)
+ progress = static_cast<double>(bytes_received) / static_cast<double>(total);
+ if (status_ != UpdateStatus::DOWNLOADING || bytes_received == total) {
+ download_progress_ = progress;
+ SetStatusAndNotify(UpdateStatus::DOWNLOADING);
+ } else {
+ ProgressUpdate(progress);
+ }
+
+ // Update the bytes downloaded in prefs.
+ int64_t current_bytes_downloaded =
+ metrics_utils::GetPersistedValue(kPrefsCurrentBytesDownloaded, prefs_);
+ int64_t total_bytes_downloaded =
+ metrics_utils::GetPersistedValue(kPrefsTotalBytesDownloaded, prefs_);
+ prefs_->SetInt64(kPrefsCurrentBytesDownloaded,
+ current_bytes_downloaded + bytes_progressed);
+ prefs_->SetInt64(kPrefsTotalBytesDownloaded,
+ total_bytes_downloaded + bytes_progressed);
+}
+
+bool UpdateAttempterAndroid::ShouldCancel(ErrorCode* cancel_reason) {
+ // TODO(deymo): Notify the DownloadAction that it should cancel the update
+ // download.
+ return false;
+}
+
+void UpdateAttempterAndroid::DownloadComplete() {
+ // Nothing needs to be done when the download completes.
+}
+
+void UpdateAttempterAndroid::ProgressUpdate(double progress) {
+ // Self throttle based on progress. Also send notifications if progress is
+ // too slow.
+ if (progress == 1.0 ||
+ progress - download_progress_ >= kBroadcastThresholdProgress ||
+ TimeTicks::Now() - last_notify_time_ >=
+ TimeDelta::FromSeconds(kBroadcastThresholdSeconds)) {
+ download_progress_ = progress;
+ SetStatusAndNotify(status_);
+ }
+}
+
+void UpdateAttempterAndroid::OnVerifyProgressUpdate(double progress) {
+ assert(status_ == UpdateStatus::VERIFYING);
+ ProgressUpdate(progress);
+}
+
+void UpdateAttempterAndroid::ScheduleProcessingStart() {
+ LOG(INFO) << "Scheduling an action processor start.";
+ brillo::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ Bind([](ActionProcessor* processor) { processor->StartProcessing(); },
+ base::Unretained(processor_.get())));
+}
+
+void UpdateAttempterAndroid::TerminateUpdateAndNotify(ErrorCode error_code) {
+ if (status_ == UpdateStatus::IDLE) {
+ LOG(ERROR) << "No ongoing update, but TerminatedUpdate() called.";
+ return;
+ }
+
+ if (status_ == UpdateStatus::CLEANUP_PREVIOUS_UPDATE) {
+ LOG(INFO) << "Terminating cleanup previous update.";
+ SetStatusAndNotify(UpdateStatus::IDLE);
+ for (auto observer : daemon_state_->service_observers())
+ observer->SendPayloadApplicationComplete(error_code);
+ return;
+ }
+
+ boot_control_->GetDynamicPartitionControl()->Cleanup();
+
+ download_progress_ = 0;
+ UpdateStatus new_status =
+ (error_code == ErrorCode::kSuccess ? UpdateStatus::UPDATED_NEED_REBOOT
+ : UpdateStatus::IDLE);
+ SetStatusAndNotify(new_status);
+ payload_fd_.reset();
+
+ // The network id is only applicable to one download attempt and once it's
+ // done the network id should not be re-used anymore.
+ if (!network_selector_->SetProcessNetwork(kDefaultNetworkId)) {
+ LOG(WARNING) << "Unable to unbind network.";
+ }
+
+ for (auto observer : daemon_state_->service_observers())
+ observer->SendPayloadApplicationComplete(error_code);
+
+ CollectAndReportUpdateMetricsOnUpdateFinished(error_code);
+ ClearMetricsPrefs();
+ if (error_code == ErrorCode::kSuccess) {
+ // We should only reset the PayloadAttemptNumber if the update succeeds, or
+ // we switch to a different payload.
+ prefs_->Delete(kPrefsPayloadAttemptNumber);
+ metrics_utils::SetSystemUpdatedMarker(clock_.get(), prefs_);
+ // Clear the total bytes downloaded if and only if the update succeeds.
+ prefs_->SetInt64(kPrefsTotalBytesDownloaded, 0);
+ }
+}
+
+void UpdateAttempterAndroid::SetStatusAndNotify(UpdateStatus status) {
+ status_ = status;
+ size_t payload_size =
+ install_plan_.payloads.empty() ? 0 : install_plan_.payloads[0].size;
+ UpdateEngineStatus status_to_send = {.status = status_,
+ .progress = download_progress_,
+ .new_size_bytes = payload_size};
+
+ for (auto observer : daemon_state_->service_observers()) {
+ observer->SendStatusUpdate(status_to_send);
+ }
+ last_notify_time_ = TimeTicks::Now();
+}
+
+void UpdateAttempterAndroid::BuildUpdateActions(HttpFetcher* fetcher) {
+ CHECK(!processor_->IsRunning());
+ processor_->set_delegate(this);
+
+ // Actions:
+ auto update_boot_flags_action =
+ std::make_unique<UpdateBootFlagsAction>(boot_control_);
+ auto cleanup_previous_update_action =
+ boot_control_->GetDynamicPartitionControl()
+ ->GetCleanupPreviousUpdateAction(boot_control_, prefs_, this);
+ auto install_plan_action = std::make_unique<InstallPlanAction>(install_plan_);
+ auto download_action =
+ std::make_unique<DownloadAction>(prefs_,
+ boot_control_,
+ hardware_,
+ nullptr, // system_state, not used.
+ fetcher, // passes ownership
+ true /* interactive */);
+ download_action->set_delegate(this);
+ download_action->set_base_offset(base_offset_);
+ auto filesystem_verifier_action = std::make_unique<FilesystemVerifierAction>(
+ boot_control_->GetDynamicPartitionControl());
+ auto postinstall_runner_action =
+ std::make_unique<PostinstallRunnerAction>(boot_control_, hardware_);
+ filesystem_verifier_action->set_delegate(this);
+ postinstall_runner_action->set_delegate(this);
+
+ // Bond them together. We have to use the leaf-types when calling
+ // BondActions().
+ BondActions(install_plan_action.get(), download_action.get());
+ BondActions(download_action.get(), filesystem_verifier_action.get());
+ BondActions(filesystem_verifier_action.get(),
+ postinstall_runner_action.get());
+
+ processor_->EnqueueAction(std::move(update_boot_flags_action));
+ processor_->EnqueueAction(std::move(cleanup_previous_update_action));
+ processor_->EnqueueAction(std::move(install_plan_action));
+ processor_->EnqueueAction(std::move(download_action));
+ processor_->EnqueueAction(std::move(filesystem_verifier_action));
+ processor_->EnqueueAction(std::move(postinstall_runner_action));
+}
+
+bool UpdateAttempterAndroid::WriteUpdateCompletedMarker() {
+ string boot_id;
+ TEST_AND_RETURN_FALSE(utils::GetBootId(&boot_id));
+ prefs_->SetString(kPrefsUpdateCompletedOnBootId, boot_id);
+ return true;
+}
+
+bool UpdateAttempterAndroid::UpdateCompletedOnThisBoot() {
+ // In case of an update_engine restart without a reboot, we stored the boot_id
+ // when the update was completed by setting a pref, so we can check whether
+ // the last update was on this boot or a previous one.
+ string boot_id;
+ TEST_AND_RETURN_FALSE(utils::GetBootId(&boot_id));
+
+ string update_completed_on_boot_id;
+ return (prefs_->Exists(kPrefsUpdateCompletedOnBootId) &&
+ prefs_->GetString(kPrefsUpdateCompletedOnBootId,
+ &update_completed_on_boot_id) &&
+ update_completed_on_boot_id == boot_id);
+}
+
+// Collect and report the android metrics when we terminate the update.
+void UpdateAttempterAndroid::CollectAndReportUpdateMetricsOnUpdateFinished(
+ ErrorCode error_code) {
+ int64_t attempt_number =
+ metrics_utils::GetPersistedValue(kPrefsPayloadAttemptNumber, prefs_);
+ PayloadType payload_type = kPayloadTypeFull;
+ int64_t payload_size = 0;
+ for (const auto& p : install_plan_.payloads) {
+ if (p.type == InstallPayloadType::kDelta)
+ payload_type = kPayloadTypeDelta;
+ payload_size += p.size;
+ }
+
+ metrics::AttemptResult attempt_result =
+ metrics_utils::GetAttemptResult(error_code);
+ Time boot_time_start = Time::FromInternalValue(
+ metrics_utils::GetPersistedValue(kPrefsUpdateBootTimestampStart, prefs_));
+ Time monotonic_time_start = Time::FromInternalValue(
+ metrics_utils::GetPersistedValue(kPrefsUpdateTimestampStart, prefs_));
+ TimeDelta duration = clock_->GetBootTime() - boot_time_start;
+ TimeDelta duration_uptime = clock_->GetMonotonicTime() - monotonic_time_start;
+
+ metrics_reporter_->ReportUpdateAttemptMetrics(
+ nullptr, // system_state
+ static_cast<int>(attempt_number),
+ payload_type,
+ duration,
+ duration_uptime,
+ payload_size,
+ attempt_result,
+ error_code);
+
+ int64_t current_bytes_downloaded =
+ metrics_utils::GetPersistedValue(kPrefsCurrentBytesDownloaded, prefs_);
+ metrics_reporter_->ReportUpdateAttemptDownloadMetrics(
+ current_bytes_downloaded,
+ 0,
+ DownloadSource::kNumDownloadSources,
+ metrics::DownloadErrorCode::kUnset,
+ metrics::ConnectionType::kUnset);
+
+ if (error_code == ErrorCode::kSuccess) {
+ int64_t reboot_count =
+ metrics_utils::GetPersistedValue(kPrefsNumReboots, prefs_);
+ string build_version;
+ prefs_->GetString(kPrefsPreviousVersion, &build_version);
+
+ // For android metrics, we only care about the total bytes downloaded
+ // for all sources; for now we assume the only download source is
+ // HttpsServer.
+ int64_t total_bytes_downloaded =
+ metrics_utils::GetPersistedValue(kPrefsTotalBytesDownloaded, prefs_);
+ int64_t num_bytes_downloaded[kNumDownloadSources] = {};
+ num_bytes_downloaded[DownloadSource::kDownloadSourceHttpsServer] =
+ total_bytes_downloaded;
+
+ int download_overhead_percentage = 0;
+ if (total_bytes_downloaded >= payload_size) {
+ CHECK_GT(payload_size, 0);
+ download_overhead_percentage =
+ (total_bytes_downloaded - payload_size) * 100ull / payload_size;
+ } else {
+ LOG(WARNING) << "Downloaded bytes " << total_bytes_downloaded
+ << " is smaller than the payload size " << payload_size;
+ }
+
+ metrics_reporter_->ReportSuccessfulUpdateMetrics(
+ static_cast<int>(attempt_number),
+ 0, // update abandoned count
+ payload_type,
+ payload_size,
+ num_bytes_downloaded,
+ download_overhead_percentage,
+ duration,
+ duration_uptime,
+ static_cast<int>(reboot_count),
+ 0); // url_switch_count
+ }
+}
+
+void UpdateAttempterAndroid::UpdatePrefsAndReportUpdateMetricsOnReboot() {
+ string current_boot_id;
+ TEST_AND_RETURN(utils::GetBootId(¤t_boot_id));
+ // Example: [ro.build.version.incremental]: [4292972]
+ string current_version =
+ android::base::GetProperty("ro.build.version.incremental", "");
+ TEST_AND_RETURN(!current_version.empty());
+
+ // If there's no record of previous version (e.g. due to a data wipe), we
+ // save the info of current boot and skip the metrics report.
+ if (!prefs_->Exists(kPrefsPreviousVersion)) {
+ prefs_->SetString(kPrefsBootId, current_boot_id);
+ prefs_->SetString(kPrefsPreviousVersion, current_version);
+ ClearMetricsPrefs();
+ return;
+ }
+ string previous_version;
+ // update_engine restarted under the same build.
+ // TODO(xunchang) identify and report rollback by checking UpdateMarker.
+ if (prefs_->GetString(kPrefsPreviousVersion, &previous_version) &&
+ previous_version == current_version) {
+ string last_boot_id;
+ bool is_reboot = prefs_->Exists(kPrefsBootId) &&
+ (prefs_->GetString(kPrefsBootId, &last_boot_id) &&
+ last_boot_id != current_boot_id);
+ // Increment the reboot number if |kPrefsNumReboots| exists. That pref is
+ // set when we start a new update.
+ if (is_reboot && prefs_->Exists(kPrefsNumReboots)) {
+ prefs_->SetString(kPrefsBootId, current_boot_id);
+ int64_t reboot_count =
+ metrics_utils::GetPersistedValue(kPrefsNumReboots, prefs_);
+ metrics_utils::SetNumReboots(reboot_count + 1, prefs_);
+ }
+ return;
+ }
+
+ // Now that the build version changes, report the update metrics.
+ // TODO(xunchang) check the build version is larger than the previous one.
+ prefs_->SetString(kPrefsBootId, current_boot_id);
+ prefs_->SetString(kPrefsPreviousVersion, current_version);
+
+ bool previous_attempt_exists = prefs_->Exists(kPrefsPayloadAttemptNumber);
+ // |kPrefsPayloadAttemptNumber| should be cleared upon successful update.
+ if (previous_attempt_exists) {
+ metrics_reporter_->ReportAbnormallyTerminatedUpdateAttemptMetrics();
+ }
+
+ metrics_utils::LoadAndReportTimeToReboot(
+ metrics_reporter_.get(), prefs_, clock_.get());
+ ClearMetricsPrefs();
+
+ // Also reset the update progress if the build version has changed.
+ if (!DeltaPerformer::ResetUpdateProgress(prefs_, false)) {
+ LOG(WARNING) << "Unable to reset the update progress.";
+ }
+}
+
+// Save the update start time. Reset the reboot count and attempt number if the
+// update isn't a resume; otherwise increment the attempt number.
+void UpdateAttempterAndroid::UpdatePrefsOnUpdateStart(bool is_resume) {
+ if (!is_resume) {
+ metrics_utils::SetNumReboots(0, prefs_);
+ metrics_utils::SetPayloadAttemptNumber(1, prefs_);
+ } else {
+ int64_t attempt_number =
+ metrics_utils::GetPersistedValue(kPrefsPayloadAttemptNumber, prefs_);
+ metrics_utils::SetPayloadAttemptNumber(attempt_number + 1, prefs_);
+ }
+ metrics_utils::SetUpdateTimestampStart(clock_->GetMonotonicTime(), prefs_);
+ metrics_utils::SetUpdateBootTimestampStart(clock_->GetBootTime(), prefs_);
+}
+
+void UpdateAttempterAndroid::ClearMetricsPrefs() {
+ CHECK(prefs_);
+ prefs_->Delete(kPrefsCurrentBytesDownloaded);
+ prefs_->Delete(kPrefsNumReboots);
+ prefs_->Delete(kPrefsSystemUpdatedMarker);
+ prefs_->Delete(kPrefsUpdateTimestampStart);
+ prefs_->Delete(kPrefsUpdateBootTimestampStart);
+}
+
+BootControlInterface::Slot UpdateAttempterAndroid::GetCurrentSlot() const {
+ return boot_control_->GetCurrentSlot();
+}
+
+BootControlInterface::Slot UpdateAttempterAndroid::GetTargetSlot() const {
+ return GetCurrentSlot() == 0 ? 1 : 0;
+}
+
+uint64_t UpdateAttempterAndroid::AllocateSpaceForPayload(
+ const std::string& metadata_filename,
+ const vector<string>& key_value_pair_headers,
+ brillo::ErrorPtr* error) {
+ DeltaArchiveManifest manifest;
+ if (!VerifyPayloadParseManifest(metadata_filename, &manifest, error)) {
+ return 0;
+ }
+ std::map<string, string> headers;
+ if (!ParseKeyValuePairHeaders(key_value_pair_headers, &headers, error)) {
+ return 0;
+ }
+
+ string payload_id = GetPayloadId(headers);
+ uint64_t required_size = 0;
+ if (!DeltaPerformer::PreparePartitionsForUpdate(prefs_,
+ boot_control_,
+ GetTargetSlot(),
+ manifest,
+ payload_id,
+ &required_size)) {
+ if (required_size == 0) {
+ LogAndSetError(error, FROM_HERE, "Failed to allocate space for payload.");
+ return 0;
+ } else {
+ LOG(ERROR) << "Insufficient space for payload: " << required_size
+ << " bytes";
+ return required_size;
+ }
+ }
+
+ LOG(INFO) << "Successfully allocated space for payload.";
+ return 0;
+}
+
+void UpdateAttempterAndroid::CleanupSuccessfulUpdate(
+ std::unique_ptr<CleanupSuccessfulUpdateCallbackInterface> callback,
+ brillo::ErrorPtr* error) {
+ if (cleanup_previous_update_code_.has_value()) {
+ LOG(INFO) << "CleanupSuccessfulUpdate has previously completed with "
+ << utils::ErrorCodeToString(*cleanup_previous_update_code_);
+ if (callback) {
+ callback->OnCleanupComplete(
+ static_cast<int32_t>(*cleanup_previous_update_code_));
+ }
+ return;
+ }
+ if (callback) {
+ auto callback_ptr = callback.get();
+ cleanup_previous_update_callbacks_.emplace_back(std::move(callback));
+ callback_ptr->RegisterForDeathNotifications(
+ base::Bind(&UpdateAttempterAndroid::RemoveCleanupPreviousUpdateCallback,
+ base::Unretained(this),
+ base::Unretained(callback_ptr)));
+ }
+ ScheduleCleanupPreviousUpdate();
+}
+
+void UpdateAttempterAndroid::ScheduleCleanupPreviousUpdate() {
+ // If a previous CleanupSuccessfulUpdate call has not finished, or an update
+ // is in progress, skip enqueueing the action.
+ if (processor_->IsRunning()) {
+ LOG(INFO) << "Already processing an update. CleanupPreviousUpdate should "
+ << "be done when the current update finishes.";
+ return;
+ }
+ LOG(INFO) << "Scheduling CleanupPreviousUpdateAction.";
+ auto action =
+ boot_control_->GetDynamicPartitionControl()
+ ->GetCleanupPreviousUpdateAction(boot_control_, prefs_, this);
+ processor_->EnqueueAction(std::move(action));
+ processor_->set_delegate(this);
+ SetStatusAndNotify(UpdateStatus::CLEANUP_PREVIOUS_UPDATE);
+ processor_->StartProcessing();
+}
+
+void UpdateAttempterAndroid::OnCleanupProgressUpdate(double progress) {
+ for (auto&& callback : cleanup_previous_update_callbacks_) {
+ callback->OnCleanupProgressUpdate(progress);
+ }
+}
+
+void UpdateAttempterAndroid::NotifyCleanupPreviousUpdateCallbacksAndClear() {
+ CHECK(cleanup_previous_update_code_.has_value());
+ for (auto&& callback : cleanup_previous_update_callbacks_) {
+ callback->OnCleanupComplete(
+ static_cast<int32_t>(*cleanup_previous_update_code_));
+ }
+ cleanup_previous_update_callbacks_.clear();
+}
+
+void UpdateAttempterAndroid::RemoveCleanupPreviousUpdateCallback(
+ CleanupSuccessfulUpdateCallbackInterface* callback) {
+ auto end_it =
+ std::remove_if(cleanup_previous_update_callbacks_.begin(),
+ cleanup_previous_update_callbacks_.end(),
+ [&](const auto& e) { return e.get() == callback; });
+ cleanup_previous_update_callbacks_.erase(
+ end_it, cleanup_previous_update_callbacks_.end());
+}
+
+} // namespace chromeos_update_engine
diff --git a/aosp/update_attempter_android.h b/aosp/update_attempter_android.h
new file mode 100644
index 0000000..499f8f6
--- /dev/null
+++ b/aosp/update_attempter_android.h
@@ -0,0 +1,249 @@
+//
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_AOSP_UPDATE_ATTEMPTER_ANDROID_H_
+#define UPDATE_ENGINE_AOSP_UPDATE_ATTEMPTER_ANDROID_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+#include <base/time/time.h>
+
+#include "update_engine/aosp/service_delegate_android_interface.h"
+#include "update_engine/client_library/include/update_engine/update_status.h"
+#include "update_engine/common/action_processor.h"
+#include "update_engine/common/boot_control_interface.h"
+#include "update_engine/common/clock.h"
+#include "update_engine/common/daemon_state_interface.h"
+#include "update_engine/common/download_action.h"
+#include "update_engine/common/hardware_interface.h"
+#include "update_engine/common/metrics_reporter_interface.h"
+#include "update_engine/common/network_selector_interface.h"
+#include "update_engine/common/prefs_interface.h"
+#include "update_engine/common/service_observer_interface.h"
+#include "update_engine/metrics_utils.h"
+#include "update_engine/payload_consumer/filesystem_verifier_action.h"
+#include "update_engine/payload_consumer/postinstall_runner_action.h"
+
+namespace chromeos_update_engine {
+
+class UpdateAttempterAndroid
+ : public ServiceDelegateAndroidInterface,
+ public ActionProcessorDelegate,
+ public DownloadActionDelegate,
+ public FilesystemVerifyDelegate,
+ public PostinstallRunnerAction::DelegateInterface,
+ public CleanupPreviousUpdateActionDelegateInterface {
+ public:
+ using UpdateStatus = update_engine::UpdateStatus;
+
+ UpdateAttempterAndroid(DaemonStateInterface* daemon_state,
+ PrefsInterface* prefs,
+ BootControlInterface* boot_control_,
+ HardwareInterface* hardware_);
+ ~UpdateAttempterAndroid() override;
+
+ // Further initialization to be done post construction.
+ void Init();
+
+ // ServiceDelegateAndroidInterface overrides.
+ bool ApplyPayload(const std::string& payload_url,
+ int64_t payload_offset,
+ int64_t payload_size,
+ const std::vector<std::string>& key_value_pair_headers,
+ brillo::ErrorPtr* error) override;
+ bool ApplyPayload(int fd,
+ int64_t payload_offset,
+ int64_t payload_size,
+ const std::vector<std::string>& key_value_pair_headers,
+ brillo::ErrorPtr* error) override;
+ bool SuspendUpdate(brillo::ErrorPtr* error) override;
+ bool ResumeUpdate(brillo::ErrorPtr* error) override;
+ bool CancelUpdate(brillo::ErrorPtr* error) override;
+ bool ResetStatus(brillo::ErrorPtr* error) override;
+ bool VerifyPayloadApplicable(const std::string& metadata_filename,
+ brillo::ErrorPtr* error) override;
+ uint64_t AllocateSpaceForPayload(
+ const std::string& metadata_filename,
+ const std::vector<std::string>& key_value_pair_headers,
+ brillo::ErrorPtr* error) override;
+ void CleanupSuccessfulUpdate(
+ std::unique_ptr<CleanupSuccessfulUpdateCallbackInterface> callback,
+ brillo::ErrorPtr* error) override;
+
+ // ActionProcessorDelegate methods:
+ void ProcessingDone(const ActionProcessor* processor,
+ ErrorCode code) override;
+ void ProcessingStopped(const ActionProcessor* processor) override;
+ void ActionCompleted(ActionProcessor* processor,
+ AbstractAction* action,
+ ErrorCode code) override;
+
+ // DownloadActionDelegate overrides.
+ void BytesReceived(uint64_t bytes_progressed,
+ uint64_t bytes_received,
+ uint64_t total) override;
+ bool ShouldCancel(ErrorCode* cancel_reason) override;
+ void DownloadComplete() override;
+
+ // FilesystemVerifyDelegate overrides
+ void OnVerifyProgressUpdate(double progress) override;
+
+ // PostinstallRunnerAction::DelegateInterface
+ void ProgressUpdate(double progress) override;
+
+ // CleanupPreviousUpdateActionDelegateInterface
+ void OnCleanupProgressUpdate(double progress) override;
+
+ private:
+ friend class UpdateAttempterAndroidTest;
+
+ // Schedules an event loop callback to start the action processor. This is
+ // scheduled asynchronously to unblock the event loop.
+ void ScheduleProcessingStart();
+
+ // Notifies an update request completed with the given error |code| to all
+ // observers.
+ void TerminateUpdateAndNotify(ErrorCode error_code);
+
+ // Sets the status to the given |status| and notifies a status update to
+ // all observers.
+ void SetStatusAndNotify(UpdateStatus status);
+
+ // Helper method to construct the sequence of actions to be performed for
+ // applying an update using a given HttpFetcher. The ownership of |fetcher| is
+ // passed to this function.
+ void BuildUpdateActions(HttpFetcher* fetcher);
+
+ // Writes to the processing completed marker. Does nothing if
+ // |update_completed_marker_| is empty.
+ bool WriteUpdateCompletedMarker();
+
+ // Returns whether an update was completed in the current boot.
+ bool UpdateCompletedOnThisBoot();
+
+ // Prefs to use for metrics report
+ // |kPrefsPayloadAttemptNumber|: number of update attempts for the current
+ // payload_id.
+ // |KprefsNumReboots|: number of reboots when applying the current update.
+ // |kPrefsSystemUpdatedMarker|: end timestamp of the last successful update.
+ // |kPrefsUpdateTimestampStart|: start timestamp in monotonic time of the
+ // current update.
+ // |kPrefsUpdateBootTimestampStart|: start timestamp in boot time of
+ // the current update.
+ // |kPrefsCurrentBytesDownloaded|: number of bytes downloaded for the current
+ // payload_id.
+ // |kPrefsTotalBytesDownloaded|: number of bytes downloaded in total since
+ // the last successful update.
+
+ // Metrics report function to call:
+ // |ReportUpdateAttemptMetrics|
+ // |ReportSuccessfulUpdateMetrics|
+ // Prefs to update:
+ // |kPrefsSystemUpdatedMarker|
+ void CollectAndReportUpdateMetricsOnUpdateFinished(ErrorCode error_code);
+
+ // Metrics report function to call:
+ // |ReportAbnormallyTerminatedUpdateAttemptMetrics|
+ // |ReportTimeToRebootMetrics|
+ // Prefs to update:
+ // |kPrefsBootId|, |kPrefsPreviousVersion|
+ void UpdatePrefsAndReportUpdateMetricsOnReboot();
+
+ // Prefs to update:
+ // |kPrefsPayloadAttemptNumber|, |kPrefsUpdateTimestampStart|,
+ // |kPrefsUpdateBootTimestampStart|
+ void UpdatePrefsOnUpdateStart(bool is_resume);
+
+ // Prefs to delete:
+ // |kPrefsNumReboots|, |kPrefsCurrentBytesDownloaded|
+ // |kPrefsSystemUpdatedMarker|, |kPrefsUpdateTimestampStart|,
+ // |kPrefsUpdateBootTimestampStart|
+ void ClearMetricsPrefs();
+
+ // Return source and target slots for update.
+ BootControlInterface::Slot GetCurrentSlot() const;
+ BootControlInterface::Slot GetTargetSlot() const;
+
+ // Helper of public VerifyPayloadApplicable. Return the parsed manifest in
+ // |manifest|.
+ static bool VerifyPayloadParseManifest(const std::string& metadata_filename,
+ DeltaArchiveManifest* manifest,
+ brillo::ErrorPtr* error);
+
+ // Enqueue and run a CleanupPreviousUpdateAction.
+ void ScheduleCleanupPreviousUpdate();
+
+ // Notify and clear |cleanup_previous_update_callbacks_|.
+ void NotifyCleanupPreviousUpdateCallbacksAndClear();
+
+ // Remove |callback| from |cleanup_previous_update_callbacks_|.
+ void RemoveCleanupPreviousUpdateCallback(
+ CleanupSuccessfulUpdateCallbackInterface* callback);
+
+ DaemonStateInterface* daemon_state_;
+
+ // DaemonStateAndroid pointers.
+ PrefsInterface* prefs_;
+ BootControlInterface* boot_control_;
+ HardwareInterface* hardware_;
+
+ // Last status notification timestamp used for throttling. Use monotonic
+ // TimeTicks to ensure that notifications are sent even if the system clock is
+ // set back in the middle of an update.
+ base::TimeTicks last_notify_time_;
+
+ // Only direct proxy supported.
+ DirectProxyResolver proxy_resolver_;
+
+ // The processor for running Actions.
+ std::unique_ptr<ActionProcessor> processor_;
+
+ // The InstallPlan used during the ongoing update.
+ InstallPlan install_plan_;
+
+ // For status:
+ UpdateStatus status_{UpdateStatus::IDLE};
+ double download_progress_{0.0};
+
+ // The offset in the payload file where the CrAU part starts.
+ int64_t base_offset_{0};
+
+ // Helper class to select the network to use during the update.
+ std::unique_ptr<NetworkSelectorInterface> network_selector_;
+
+ std::unique_ptr<ClockInterface> clock_;
+
+ std::unique_ptr<MetricsReporterInterface> metrics_reporter_;
+
+ ::android::base::unique_fd payload_fd_;
+
+ std::vector<std::unique_ptr<CleanupSuccessfulUpdateCallbackInterface>>
+ cleanup_previous_update_callbacks_;
+ // Result of previous CleanupPreviousUpdateAction. Nullopt If
+ // CleanupPreviousUpdateAction has not been executed.
+ std::optional<ErrorCode> cleanup_previous_update_code_{std::nullopt};
+
+ DISALLOW_COPY_AND_ASSIGN(UpdateAttempterAndroid);
+};
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_AOSP_UPDATE_ATTEMPTER_ANDROID_H_
diff --git a/aosp/update_attempter_android_unittest.cc b/aosp/update_attempter_android_unittest.cc
new file mode 100644
index 0000000..bb44450
--- /dev/null
+++ b/aosp/update_attempter_android_unittest.cc
@@ -0,0 +1,228 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/aosp/update_attempter_android.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include <android-base/properties.h>
+#include <base/time/time.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/aosp/daemon_state_android.h"
+#include "update_engine/common/fake_boot_control.h"
+#include "update_engine/common/fake_clock.h"
+#include "update_engine/common/fake_hardware.h"
+#include "update_engine/common/fake_prefs.h"
+#include "update_engine/common/mock_action_processor.h"
+#include "update_engine/common/mock_metrics_reporter.h"
+#include "update_engine/common/test_utils.h"
+#include "update_engine/common/utils.h"
+
+using base::Time;
+using base::TimeDelta;
+using testing::_;
+using update_engine::UpdateStatus;
+
+namespace chromeos_update_engine {
+
+class UpdateAttempterAndroidTest : public ::testing::Test {
+ protected:
+ UpdateAttempterAndroidTest() = default;
+
+ void SetUp() override {
+ clock_ = new FakeClock();
+ metrics_reporter_ = new testing::NiceMock<MockMetricsReporter>();
+ update_attempter_android_.metrics_reporter_.reset(metrics_reporter_);
+ update_attempter_android_.clock_.reset(clock_);
+ update_attempter_android_.processor_.reset(
+ new testing::NiceMock<MockActionProcessor>());
+ }
+
+ void SetUpdateStatus(update_engine::UpdateStatus status) {
+ update_attempter_android_.status_ = status;
+ }
+
+ void AddPayload(InstallPlan::Payload&& payload) {
+ update_attempter_android_.install_plan_.payloads.push_back(
+ std::move(payload));
+ }
+
+ UpdateAttempterAndroid update_attempter_android_{
+ &daemon_state_, &prefs_, &boot_control_, &hardware_};
+
+ DaemonStateAndroid daemon_state_;
+ FakePrefs prefs_;
+ FakeBootControl boot_control_;
+ FakeHardware hardware_;
+
+ FakeClock* clock_;
+ testing::NiceMock<MockMetricsReporter>* metrics_reporter_;
+};
+
+TEST_F(UpdateAttempterAndroidTest, UpdatePrefsSameBuildVersionOnInit) {
+ std::string build_version =
+ android::base::GetProperty("ro.build.version.incremental", "");
+ prefs_.SetString(kPrefsPreviousVersion, build_version);
+ prefs_.SetString(kPrefsBootId, "oldboot");
+ prefs_.SetInt64(kPrefsNumReboots, 1);
+
+ EXPECT_CALL(*metrics_reporter_, ReportTimeToReboot(_)).Times(0);
+ update_attempter_android_.Init();
+
+ // Check that the boot_id and reboot_count are updated.
+ std::string boot_id;
+ utils::GetBootId(&boot_id);
+ EXPECT_TRUE(prefs_.Exists(kPrefsBootId));
+ std::string prefs_boot_id;
+ EXPECT_TRUE(prefs_.GetString(kPrefsBootId, &prefs_boot_id));
+ EXPECT_EQ(boot_id, prefs_boot_id);
+
+ EXPECT_TRUE(prefs_.Exists(kPrefsNumReboots));
+ int64_t reboot_count;
+ EXPECT_TRUE(prefs_.GetInt64(kPrefsNumReboots, &reboot_count));
+ EXPECT_EQ(2, reboot_count);
+}
+
+TEST_F(UpdateAttempterAndroidTest, UpdatePrefsBuildVersionChangeOnInit) {
+ prefs_.SetString(kPrefsPreviousVersion, "00001"); // Set the fake version
+ prefs_.SetInt64(kPrefsPayloadAttemptNumber, 1);
+ prefs_.SetInt64(kPrefsSystemUpdatedMarker, 23456);
+
+ EXPECT_CALL(*metrics_reporter_,
+ ReportAbnormallyTerminatedUpdateAttemptMetrics())
+ .Times(1);
+
+ Time now = Time::FromInternalValue(34456);
+ clock_->SetMonotonicTime(now);
+ TimeDelta duration = now - Time::FromInternalValue(23456);
+ EXPECT_CALL(*metrics_reporter_, ReportTimeToReboot(duration.InMinutes()))
+ .Times(1);
+
+ update_attempter_android_.Init();
+ // Check that we reset the metric prefs.
+ EXPECT_FALSE(prefs_.Exists(kPrefsNumReboots));
+ EXPECT_FALSE(prefs_.Exists(kPrefsUpdateTimestampStart));
+ EXPECT_FALSE(prefs_.Exists(kPrefsSystemUpdatedMarker));
+ // PayloadAttemptNumber should persist across reboots.
+ EXPECT_TRUE(prefs_.Exists(kPrefsPayloadAttemptNumber));
+}
+
+TEST_F(UpdateAttempterAndroidTest, ReportMetricsOnUpdateTerminated) {
+ prefs_.SetInt64(kPrefsNumReboots, 3);
+ prefs_.SetInt64(kPrefsPayloadAttemptNumber, 2);
+ prefs_.SetString(kPrefsPreviousVersion, "56789");
+ prefs_.SetInt64(kPrefsUpdateBootTimestampStart, 10000);
+ prefs_.SetInt64(kPrefsUpdateTimestampStart, 12345);
+
+ Time boot_time = Time::FromInternalValue(22345);
+ Time up_time = Time::FromInternalValue(21345);
+ clock_->SetBootTime(boot_time);
+ clock_->SetMonotonicTime(up_time);
+ TimeDelta duration = boot_time - Time::FromInternalValue(10000);
+ TimeDelta duration_uptime = up_time - Time::FromInternalValue(12345);
+ EXPECT_CALL(
+ *metrics_reporter_,
+ ReportUpdateAttemptMetrics(_,
+ 2,
+ _,
+ duration,
+ duration_uptime,
+ _,
+ metrics::AttemptResult::kUpdateSucceeded,
+ ErrorCode::kSuccess))
+ .Times(1);
+ EXPECT_CALL(*metrics_reporter_,
+ ReportSuccessfulUpdateMetrics(
+ 2, 0, _, 50, _, _, duration, duration_uptime, 3, _))
+ .Times(1);
+
+ // Adds a payload of 50 bytes to the InstallPlan.
+ InstallPlan::Payload payload;
+ payload.size = 50;
+ AddPayload(std::move(payload));
+ SetUpdateStatus(UpdateStatus::UPDATE_AVAILABLE);
+ update_attempter_android_.ProcessingDone(nullptr, ErrorCode::kSuccess);
+
+ EXPECT_FALSE(prefs_.Exists(kPrefsNumReboots));
+ EXPECT_FALSE(prefs_.Exists(kPrefsPayloadAttemptNumber));
+ EXPECT_FALSE(prefs_.Exists(kPrefsUpdateTimestampStart));
+ EXPECT_TRUE(prefs_.Exists(kPrefsSystemUpdatedMarker));
+}
+
+TEST_F(UpdateAttempterAndroidTest, ReportMetricsForBytesDownloaded) {
+ // Check both prefs are updated correctly.
+ update_attempter_android_.BytesReceived(20, 50, 200);
+ EXPECT_EQ(
+ 20,
+ metrics_utils::GetPersistedValue(kPrefsCurrentBytesDownloaded, &prefs_));
+ EXPECT_EQ(
+ 20,
+ metrics_utils::GetPersistedValue(kPrefsTotalBytesDownloaded, &prefs_));
+
+ EXPECT_CALL(*metrics_reporter_,
+ ReportUpdateAttemptDownloadMetrics(50, _, _, _, _))
+ .Times(1);
+ EXPECT_CALL(*metrics_reporter_,
+ ReportUpdateAttemptDownloadMetrics(40, _, _, _, _))
+ .Times(1);
+
+ int64_t total_bytes[kNumDownloadSources] = {};
+ total_bytes[kDownloadSourceHttpsServer] = 90;
+ EXPECT_CALL(*metrics_reporter_,
+ ReportSuccessfulUpdateMetrics(
+ _,
+ _,
+ _,
+ 50,
+ test_utils::DownloadSourceMatcher(total_bytes),
+ 80,
+ _,
+ _,
+ _,
+ _))
+ .Times(1);
+
+ // Adds a payload of 50 bytes to the InstallPlan.
+ InstallPlan::Payload payload;
+ payload.size = 50;
+ AddPayload(std::move(payload));
+
+ // The first update fails after receiving 50 bytes in total.
+ update_attempter_android_.BytesReceived(30, 50, 200);
+ update_attempter_android_.ProcessingDone(nullptr, ErrorCode::kError);
+ EXPECT_EQ(
+ 0,
+ metrics_utils::GetPersistedValue(kPrefsCurrentBytesDownloaded, &prefs_));
+ EXPECT_EQ(
+ 50,
+ metrics_utils::GetPersistedValue(kPrefsTotalBytesDownloaded, &prefs_));
+
+ // The second update succeeds after receiving 40 bytes, which leads to a
+ // overhead of (90 - 50) / 50 = 80%.
+ update_attempter_android_.BytesReceived(40, 40, 50);
+ update_attempter_android_.ProcessingDone(nullptr, ErrorCode::kSuccess);
+ // Both prefs should be cleared.
+ EXPECT_EQ(
+ 0,
+ metrics_utils::GetPersistedValue(kPrefsCurrentBytesDownloaded, &prefs_));
+ EXPECT_EQ(
+ 0, metrics_utils::GetPersistedValue(kPrefsTotalBytesDownloaded, &prefs_));
+}
+
+} // namespace chromeos_update_engine
diff --git a/aosp/update_engine_client_android.cc b/aosp/update_engine_client_android.cc
new file mode 100644
index 0000000..1a68cf4
--- /dev/null
+++ b/aosp/update_engine_client_android.cc
@@ -0,0 +1,314 @@
+//
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include <sysexits.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include <base/bind.h>
+#include <base/callback.h>
+#include <base/command_line.h>
+#include <base/logging.h>
+#include <base/strings/string_split.h>
+#include <binder/IServiceManager.h>
+#include <binderwrapper/binder_wrapper.h>
+#include <brillo/binder_watcher.h>
+#include <brillo/daemons/daemon.h>
+#include <brillo/flag_helper.h>
+#include <brillo/message_loops/message_loop.h>
+#include <brillo/syslog_logging.h>
+#include <utils/String16.h>
+#include <utils/StrongPointer.h>
+
+#include "android/os/BnUpdateEngineCallback.h"
+#include "android/os/IUpdateEngine.h"
+#include "update_engine/client_library/include/update_engine/update_status.h"
+#include "update_engine/common/error_code.h"
+#include "update_engine/common/error_code_utils.h"
+#include "update_engine/update_status_utils.h"
+
+using android::binder::Status;
+
+namespace chromeos_update_engine {
+namespace internal {
+
+class UpdateEngineClientAndroid : public brillo::Daemon {
+ public:
+ UpdateEngineClientAndroid(int argc, char** argv) : argc_(argc), argv_(argv) {}
+
+ int ExitWhenIdle(const Status& status);
+ int ExitWhenIdle(int return_code);
+
+ private:
+ class UECallback : public android::os::BnUpdateEngineCallback {
+ public:
+ explicit UECallback(UpdateEngineClientAndroid* client) : client_(client) {}
+
+ // android::os::BnUpdateEngineCallback overrides.
+ Status onStatusUpdate(int status_code, float progress) override;
+ Status onPayloadApplicationComplete(int error_code) override;
+
+ private:
+ UpdateEngineClientAndroid* client_;
+ };
+
+ int OnInit() override;
+
+ // Called whenever the UpdateEngine daemon dies.
+ void UpdateEngineServiceDied();
+
+ static std::vector<android::String16> ParseHeaders(const std::string& arg);
+
+ // Copy of argc and argv passed to main().
+ int argc_;
+ char** argv_;
+
+ android::sp<android::os::IUpdateEngine> service_;
+ android::sp<android::os::BnUpdateEngineCallback> callback_;
+ android::sp<android::os::BnUpdateEngineCallback> cleanup_callback_;
+
+ brillo::BinderWatcher binder_watcher_;
+};
+
+Status UpdateEngineClientAndroid::UECallback::onStatusUpdate(int status_code,
+ float progress) {
+ update_engine::UpdateStatus status =
+ static_cast<update_engine::UpdateStatus>(status_code);
+ LOG(INFO) << "onStatusUpdate(" << UpdateStatusToString(status) << " ("
+ << status_code << "), " << progress << ")";
+ return Status::ok();
+}
+
+Status UpdateEngineClientAndroid::UECallback::onPayloadApplicationComplete(
+ int error_code) {
+ ErrorCode code = static_cast<ErrorCode>(error_code);
+ LOG(INFO) << "onPayloadApplicationComplete(" << utils::ErrorCodeToString(code)
+ << " (" << error_code << "))";
+ client_->ExitWhenIdle(
+ (code == ErrorCode::kSuccess || code == ErrorCode::kUpdatedButNotActive)
+ ? EX_OK
+ : 1);
+ return Status::ok();
+}
+
+int UpdateEngineClientAndroid::OnInit() {
+ int ret = Daemon::OnInit();
+ if (ret != EX_OK)
+ return ret;
+
+ DEFINE_bool(update, false, "Start a new update, if no update in progress.");
+ DEFINE_string(payload,
+ "http://127.0.0.1:8080/payload",
+ "The URI to the update payload to use.");
+ DEFINE_int64(offset,
+ 0,
+ "The offset in the payload where the CrAU update starts. "
+ "Used when --update is passed.");
+ DEFINE_int64(size,
+ 0,
+ "The size of the CrAU part of the payload. If 0 is passed, it "
+ "will be autodetected. Used when --update is passed.");
+ DEFINE_string(headers,
+ "",
+ "A list of key-value pairs, one element of the list per line. "
+ "Used when --update or --allocate is passed.");
+
+ DEFINE_bool(verify,
+ false,
+ "Given payload metadata, verify if the payload is applicable.");
+ DEFINE_bool(allocate, false, "Given payload metadata, allocate space.");
+ DEFINE_string(metadata,
+ "/data/ota_package/metadata",
+ "The path to the update payload metadata. "
+ "Used when --verify or --allocate is passed.");
+
+ DEFINE_bool(suspend, false, "Suspend an ongoing update and exit.");
+ DEFINE_bool(resume, false, "Resume a suspended update.");
+ DEFINE_bool(cancel, false, "Cancel the ongoing update and exit.");
+ DEFINE_bool(reset_status, false, "Reset an already applied update and exit.");
+ DEFINE_bool(follow,
+ false,
+ "Follow status update changes until a final state is reached. "
+ "Exit status is 0 if the update succeeded, and 1 otherwise.");
+ DEFINE_bool(merge,
+ false,
+ "Wait for previous update to merge. "
+ "Only available after rebooting to new slot.");
+ // Boilerplate init commands.
+ base::CommandLine::Init(argc_, argv_);
+ brillo::FlagHelper::Init(argc_, argv_, "Android Update Engine Client");
+ if (argc_ == 1) {
+ LOG(ERROR) << "Nothing to do. Run with --help for help.";
+ return 1;
+ }
+
+ // Ensure there are no positional arguments.
+ const std::vector<std::string> positional_args =
+ base::CommandLine::ForCurrentProcess()->GetArgs();
+ if (!positional_args.empty()) {
+ LOG(ERROR) << "Found a positional argument '" << positional_args.front()
+ << "'. If you want to pass a value to a flag, pass it as "
+ "--flag=value.";
+ return 1;
+ }
+
+ bool keep_running = false;
+ brillo::InitLog(brillo::kLogToStderr);
+
+ // Initialize a binder watcher early in the process before any interaction
+ // with the binder driver.
+ binder_watcher_.Init();
+
+ android::status_t status = android::getService(
+ android::String16("android.os.UpdateEngineService"), &service_);
+ if (status != android::OK) {
+ LOG(ERROR) << "Failed to get IUpdateEngine binder from service manager: "
+ << Status::fromStatusT(status).toString8();
+ return ExitWhenIdle(1);
+ }
+
+ if (FLAGS_suspend) {
+ return ExitWhenIdle(service_->suspend());
+ }
+
+ if (FLAGS_resume) {
+ return ExitWhenIdle(service_->resume());
+ }
+
+ if (FLAGS_cancel) {
+ return ExitWhenIdle(service_->cancel());
+ }
+
+ if (FLAGS_reset_status) {
+ return ExitWhenIdle(service_->resetStatus());
+ }
+
+ if (FLAGS_verify) {
+ bool applicable = false;
+ Status status = service_->verifyPayloadApplicable(
+ android::String16{FLAGS_metadata.data(), FLAGS_metadata.size()},
+ &applicable);
+ LOG(INFO) << "Payload is " << (applicable ? "" : "not ") << "applicable.";
+ return ExitWhenIdle(status);
+ }
+
+ if (FLAGS_allocate) {
+ auto headers = ParseHeaders(FLAGS_headers);
+ int64_t ret = 0;
+ Status status = service_->allocateSpaceForPayload(
+ android::String16{FLAGS_metadata.data(), FLAGS_metadata.size()},
+ headers,
+ &ret);
+ if (status.isOk()) {
+ if (ret == 0) {
+ LOG(INFO) << "Successfully allocated space for payload.";
+ } else {
+ LOG(INFO) << "Insufficient space; required " << ret << " bytes.";
+ }
+ } else {
+ LOG(INFO) << "Allocation failed.";
+ }
+ return ExitWhenIdle(status);
+ }
+
+ if (FLAGS_merge) {
+ // Register a callback object with the service.
+ cleanup_callback_ = new UECallback(this);
+ Status status = service_->cleanupSuccessfulUpdate(cleanup_callback_);
+ if (!status.isOk()) {
+ LOG(ERROR) << "Failed to call cleanupSuccessfulUpdate.";
+ return ExitWhenIdle(status);
+ }
+ keep_running = true;
+ }
+
+ if (FLAGS_follow) {
+ // Register a callback object with the service.
+ callback_ = new UECallback(this);
+ bool bound;
+ if (!service_->bind(callback_, &bound).isOk() || !bound) {
+ LOG(ERROR) << "Failed to bind() the UpdateEngine daemon.";
+ return 1;
+ }
+ keep_running = true;
+ }
+
+ if (FLAGS_update) {
+ auto and_headers = ParseHeaders(FLAGS_headers);
+ Status status = service_->applyPayload(
+ android::String16{FLAGS_payload.data(), FLAGS_payload.size()},
+ FLAGS_offset,
+ FLAGS_size,
+ and_headers);
+ if (!status.isOk())
+ return ExitWhenIdle(status);
+ }
+
+ if (!keep_running)
+ return ExitWhenIdle(EX_OK);
+
+ // When following updates status changes, exit if the update_engine daemon
+ // dies.
+ android::BinderWrapper::Create();
+ android::BinderWrapper::Get()->RegisterForDeathNotifications(
+ android::os::IUpdateEngine::asBinder(service_),
+ base::Bind(&UpdateEngineClientAndroid::UpdateEngineServiceDied,
+ base::Unretained(this)));
+
+ return EX_OK;
+}
+
+int UpdateEngineClientAndroid::ExitWhenIdle(const Status& status) {
+ if (status.isOk())
+ return ExitWhenIdle(EX_OK);
+ LOG(ERROR) << status.toString8();
+ return ExitWhenIdle(status.exceptionCode());
+}
+
+int UpdateEngineClientAndroid::ExitWhenIdle(int return_code) {
+ auto delayed_exit = base::Bind(
+ &Daemon::QuitWithExitCode, base::Unretained(this), return_code);
+ if (!brillo::MessageLoop::current()->PostTask(delayed_exit))
+ return 1;
+ return EX_OK;
+}
+
+void UpdateEngineClientAndroid::UpdateEngineServiceDied() {
+ LOG(ERROR) << "UpdateEngineService died.";
+ QuitWithExitCode(1);
+}
+
+std::vector<android::String16> UpdateEngineClientAndroid::ParseHeaders(
+ const std::string& arg) {
+ std::vector<std::string> headers = base::SplitString(
+ arg, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+ std::vector<android::String16> and_headers;
+ for (const auto& header : headers) {
+ and_headers.push_back(android::String16{header.data(), header.size()});
+ }
+ return and_headers;
+}
+
+} // namespace internal
+} // namespace chromeos_update_engine
+
+int main(int argc, char** argv) {
+ chromeos_update_engine::internal::UpdateEngineClientAndroid client(argc,
+ argv);
+ return client.Run();
+}