Merge Android U (ab/10368041)
Bug: 291102124
Merged-In: I4c2345c227697f76adf8623bb4bd57bcfd97901f
Change-Id: I1a6a4fc7ba09b97ef605ab3ec13f4b4b8656f1b3
diff --git a/Android.bp b/Android.bp
index a33aa8f..4db429d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -75,12 +75,6 @@
],
ldflags: ["-Wl,--gc-sections"],
- product_variables: {
- pdk: {
- enabled: false,
- },
- },
-
target: {
android: {
cflags: [
@@ -348,8 +342,7 @@
"libcutils",
"libfs_mgr_binder",
"libgsi",
- "libsnapshot",
- "libsnapshot_cow",
+ "libsnapshot_static",
"libstatslog_ue",
"libz",
],
@@ -375,7 +368,7 @@
],
exclude_static_libs: [
"libfs_mgr_binder",
- "libsnapshot",
+ "libsnapshot_static",
"libstatslog_ue"
],
},
@@ -1048,6 +1041,8 @@
"unittest_key_RSA4096.pem",
"unittest_key_EC.pem",
"update_engine.conf",
+ "testdata/cycle_nodes_product.bin",
+ "testdata/cycle_nodes_product_no_xor.bin",
],
static_libs: [
"libgmock",
@@ -1164,6 +1159,8 @@
"unittest_key_RSA4096.pem",
"unittest_key_EC.pem",
"update_engine.conf",
+ "testdata/cycle_nodes_product.bin",
+ "testdata/cycle_nodes_product_no_xor.bin",
],
// We cannot use the default generated AndroidTest.xml because of the use of helper modules
diff --git a/aosp/binder_service_android.cc b/aosp/binder_service_android.cc
index 84b5b7a..37df9a5 100644
--- a/aosp/binder_service_android.cc
+++ b/aosp/binder_service_android.cc
@@ -21,7 +21,6 @@
#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"
@@ -78,10 +77,7 @@
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())));
+ [this, callback = callback_binder.get()]() { UnbindCallback(callback); });
*return_value = true;
return Status::ok();
@@ -103,10 +99,10 @@
int64_t payload_offset,
int64_t payload_size,
const vector<android::String16>& header_kv_pairs) {
- const string payload_url{android::String8{url}.string()};
+ const string payload_url{android::String8{url}.c_str()};
vector<string> str_headers = ToVecString(header_kv_pairs);
- brillo::ErrorPtr error;
+ Error error;
if (!service_delegate_->ApplyPayload(
payload_url, payload_offset, payload_size, str_headers, &error)) {
return ErrorPtrToStatus(error);
@@ -121,7 +117,7 @@
const vector<android::String16>& header_kv_pairs) {
vector<string> str_headers = ToVecString(header_kv_pairs);
- brillo::ErrorPtr error;
+ Error error;
if (!service_delegate_->ApplyPayload(
pfd.get(), payload_offset, payload_size, str_headers, &error)) {
return ErrorPtrToStatus(error);
@@ -130,28 +126,28 @@
}
Status BinderUpdateEngineAndroidService::suspend() {
- brillo::ErrorPtr error;
+ Error error;
if (!service_delegate_->SuspendUpdate(&error))
return ErrorPtrToStatus(error);
return Status::ok();
}
Status BinderUpdateEngineAndroidService::resume() {
- brillo::ErrorPtr error;
+ Error error;
if (!service_delegate_->ResumeUpdate(&error))
return ErrorPtrToStatus(error);
return Status::ok();
}
Status BinderUpdateEngineAndroidService::cancel() {
- brillo::ErrorPtr error;
+ Error error;
if (!service_delegate_->CancelUpdate(&error))
return ErrorPtrToStatus(error);
return Status::ok();
}
Status BinderUpdateEngineAndroidService::resetStatus() {
- brillo::ErrorPtr error;
+ Error error;
if (!service_delegate_->ResetStatus(&error))
return ErrorPtrToStatus(error);
return Status::ok();
@@ -159,16 +155,16 @@
Status BinderUpdateEngineAndroidService::setShouldSwitchSlotOnReboot(
const android::String16& metadata_filename) {
- brillo::ErrorPtr error;
+ Error error;
if (!service_delegate_->setShouldSwitchSlotOnReboot(
- android::String8(metadata_filename).string(), &error)) {
+ android::String8(metadata_filename).c_str(), &error)) {
return ErrorPtrToStatus(error);
}
return Status::ok();
}
Status BinderUpdateEngineAndroidService::resetShouldSwitchSlotOnReboot() {
- brillo::ErrorPtr error;
+ Error error;
if (!service_delegate_->resetShouldSwitchSlotOnReboot(&error)) {
return ErrorPtrToStatus(error);
}
@@ -178,13 +174,13 @@
Status BinderUpdateEngineAndroidService::verifyPayloadApplicable(
const android::String16& metadata_filename, bool* return_value) {
const std::string payload_metadata{
- android::String8{metadata_filename}.string()};
+ android::String8{metadata_filename}.c_str()};
LOG(INFO) << "Received a request of verifying payload metadata in "
<< payload_metadata << ".";
- brillo::ErrorPtr error;
+ Error error;
*return_value =
service_delegate_->VerifyPayloadApplicable(payload_metadata, &error);
- if (error != nullptr)
+ if (error.error_code != ErrorCode::kSuccess)
return ErrorPtrToStatus(error);
return Status::ok();
}
@@ -209,15 +205,15 @@
const vector<android::String16>& header_kv_pairs,
int64_t* return_value) {
const std::string payload_metadata{
- android::String8{metadata_filename}.string()};
+ android::String8{metadata_filename}.c_str()};
vector<string> str_headers = ToVecString(header_kv_pairs);
LOG(INFO) << "Received a request of allocating space for " << payload_metadata
<< ".";
- brillo::ErrorPtr error;
+ Error error;
*return_value =
static_cast<int64_t>(service_delegate_->AllocateSpaceForPayload(
payload_metadata, str_headers, &error));
- if (error != nullptr)
+ if (error.error_code != ErrorCode::kSuccess)
return ErrorPtrToStatus(error);
return Status::ok();
}
@@ -237,7 +233,7 @@
update_engine::UpdateStatus::CLEANUP_PREVIOUS_UPDATE),
progress));
}
- void RegisterForDeathNotifications(base::Closure unbind) {
+ void RegisterForDeathNotifications(const std::function<void()>& unbind) {
const android::sp<android::IBinder>& callback_binder =
IUpdateEngineCallback::asBinder(callback_);
auto binder_wrapper = android::BinderWrapper::Get();
@@ -250,10 +246,10 @@
Status BinderUpdateEngineAndroidService::cleanupSuccessfulUpdate(
const android::sp<IUpdateEngineCallback>& callback) {
- brillo::ErrorPtr error;
+ Error error;
service_delegate_->CleanupSuccessfulUpdate(
std::make_unique<CleanupSuccessfulUpdateCallback>(callback), &error);
- if (error != nullptr)
+ if (error.error_code != ErrorCode::kSuccess)
return ErrorPtrToStatus(error);
return Status::ok();
}
diff --git a/aosp/binder_service_android_common.h b/aosp/binder_service_android_common.h
index 223b32e..636e98e 100644
--- a/aosp/binder_service_android_common.h
+++ b/aosp/binder_service_android_common.h
@@ -20,14 +20,16 @@
#include <string>
#include <vector>
+#include "update_engine/common/error.h"
+
#include <binder/Status.h>
namespace chromeos_update_engine {
-static inline android::binder::Status ErrorPtrToStatus(
- const brillo::ErrorPtr& error) {
+static inline android::binder::Status ErrorPtrToStatus(const Error& error) {
return android::binder::Status::fromServiceSpecificError(
- 1, android::String8{error->GetMessage().c_str()});
+ static_cast<int>(error.error_code),
+ android::String8{error.message.c_str()});
}
static inline std::vector<std::string> ToVecString(
@@ -35,7 +37,7 @@
std::vector<std::string> out;
out.reserve(inp.size());
for (const auto& e : inp) {
- out.emplace_back(android::String8{e}.string());
+ out.emplace_back(android::String8{e}.c_str());
}
return out;
}
diff --git a/aosp/binder_service_stable_android.cc b/aosp/binder_service_stable_android.cc
index 17b35ee..3bc7f6c 100644
--- a/aosp/binder_service_stable_android.cc
+++ b/aosp/binder_service_stable_android.cc
@@ -21,7 +21,6 @@
#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"
@@ -83,10 +82,7 @@
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())));
+ [this, callback = callback_binder.get()]() { UnbindCallback(callback); });
*return_value = true;
return Status::ok();
@@ -111,7 +107,7 @@
const vector<android::String16>& header_kv_pairs) {
vector<string> str_headers = ToVecString(header_kv_pairs);
- brillo::ErrorPtr error;
+ Error error;
if (!service_delegate_->ApplyPayload(
pfd.get(), payload_offset, payload_size, str_headers, &error)) {
return ErrorPtrToStatus(error);
diff --git a/aosp/dynamic_partition_control_android.cc b/aosp/dynamic_partition_control_android.cc
index a309b6e..d8df520 100644
--- a/aosp/dynamic_partition_control_android.cc
+++ b/aosp/dynamic_partition_control_android.cc
@@ -48,6 +48,7 @@
#include "update_engine/aosp/dynamic_partition_utils.h"
#include "update_engine/common/boot_control_interface.h"
#include "update_engine/common/dynamic_partition_control_interface.h"
+#include "update_engine/common/error_code.h"
#include "update_engine/common/platform_constants.h"
#include "update_engine/common/utils.h"
#include "update_engine/payload_consumer/cow_writer_file_descriptor.h"
@@ -449,7 +450,8 @@
uint32_t target_slot,
const DeltaArchiveManifest& manifest,
bool update,
- uint64_t* required_size) {
+ uint64_t* required_size,
+ ErrorCode* error) {
source_slot_ = source_slot;
target_slot_ = target_slot;
if (required_size != nullptr) {
@@ -458,10 +460,14 @@
if (fs_mgr_overlayfs_is_setup()) {
// Non DAP devices can use overlayfs as well.
- LOG(WARNING)
+ LOG(ERROR)
<< "overlayfs overrides are active and can interfere with our "
"resources.\n"
<< "run adb enable-verity to deactivate if required and try again.";
+ if (error) {
+ *error = ErrorCode::kOverlayfsenabledError;
+ return false;
+ }
}
// If metadata is erased but not formatted, it is possible to not mount
@@ -852,6 +858,7 @@
auto target_device =
device_dir.Append(GetSuperPartitionName(target_slot)).value();
+
return StoreMetadata(target_device, builder.get(), target_slot);
}
diff --git a/aosp/dynamic_partition_control_android.h b/aosp/dynamic_partition_control_android.h
index 830901b..35a79fd 100644
--- a/aosp/dynamic_partition_control_android.h
+++ b/aosp/dynamic_partition_control_android.h
@@ -55,7 +55,8 @@
uint32_t target_slot,
const DeltaArchiveManifest& manifest,
bool update,
- uint64_t* required_size) override;
+ uint64_t* required_size,
+ ErrorCode* error = nullptr) override;
bool FinishUpdate(bool powerwash_required) override;
std::unique_ptr<AbstractAction> GetCleanupPreviousUpdateAction(
BootControlInterface* boot_control,
diff --git a/aosp/dynamic_partition_control_android_unittest.cc b/aosp/dynamic_partition_control_android_unittest.cc
index 6f1d4ef..30780f0 100644
--- a/aosp/dynamic_partition_control_android_unittest.cc
+++ b/aosp/dynamic_partition_control_android_unittest.cc
@@ -164,6 +164,7 @@
target(),
PartitionSizesToManifest(partition_sizes),
true,
+ nullptr,
nullptr);
}
void SetSlots(const TestParam& slots) { slots_ = slots; }
@@ -363,7 +364,7 @@
// DynamicPartitionControlAndroidTest::PreparePartitionsForUpdate(), since we
// don't want any default group in the PartitionMetadata.
EXPECT_TRUE(dynamicControl().PreparePartitionsForUpdate(
- source(), target(), {}, true, nullptr));
+ source(), target(), {}, true, nullptr, nullptr));
// Should use dynamic source partitions.
EXPECT_CALL(dynamicControl(), GetState(S("system")))
@@ -504,6 +505,7 @@
target(),
PartitionSizesToManifest({{"system", 2_GiB}, {"vendor", 1_GiB}}),
false,
+ nullptr,
nullptr));
// Dynamic partition "system".
@@ -759,6 +761,7 @@
target(),
PartitionSizesToManifest({{"foo", 4_MiB}}),
false,
+ nullptr,
nullptr));
dynamicControl().set_fake_mapped_devices({T("foo")});
@@ -1041,8 +1044,12 @@
}));
}
bool PreparePartitionsForUpdate(uint64_t* required_size) {
- return dynamicControl().PreparePartitionsForUpdate(
- source(), target(), manifest_, true /* update */, required_size);
+ return dynamicControl().PreparePartitionsForUpdate(source(),
+ target(),
+ manifest_,
+ true /* update */,
+ required_size,
+ nullptr);
}
MockSnapshotManager* snapshot_ = nullptr;
DeltaArchiveManifest manifest_;
diff --git a/aosp/service_delegate_android_interface.h b/aosp/service_delegate_android_interface.h
index b3660ab..45c0274 100644
--- a/aosp/service_delegate_android_interface.h
+++ b/aosp/service_delegate_android_interface.h
@@ -23,7 +23,7 @@
#include <string>
#include <vector>
-#include <brillo/errors/error.h>
+#include "update_engine/common/error.h"
namespace chromeos_update_engine {
@@ -36,7 +36,8 @@
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;
+ virtual void RegisterForDeathNotifications(
+ const std::function<void()>& unbind) = 0;
};
// This class defines the interface exposed by the Android version of the
@@ -58,54 +59,54 @@
int64_t payload_offset,
int64_t payload_size,
const std::vector<std::string>& key_value_pair_headers,
- brillo::ErrorPtr* error) = 0;
+ Error* 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;
+ Error* 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;
+ virtual bool SuspendUpdate(Error* 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;
+ virtual bool ResumeUpdate(Error* 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;
+ virtual bool CancelUpdate(Error* 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;
+ virtual bool ResetStatus(Error* 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;
+ Error* error) = 0;
// Sets the A/B slot switch for the next boot after applying an ota update.
// If applyPayload hasn't switched the slot by itself, the client can call
// this API to switch the slot and apply the update on next boot. Returns
// true on success.
virtual bool setShouldSwitchSlotOnReboot(const std::string& metadata_filename,
- brillo::ErrorPtr* error) = 0;
+ Error* error) = 0;
// Resets the boot slot to the source/current slot, without cancelling the
// update progress. This can be called after the update is installed, and to
// prevent the device from accidentally taking the update when it reboots.
- virtual bool resetShouldSwitchSlotOnReboot(brillo::ErrorPtr* error) = 0;
+ virtual bool resetShouldSwitchSlotOnReboot(Error* error) = 0;
// Allocates space for a payload.
// Returns 0 if space is successfully preallocated.
@@ -118,7 +119,7 @@
virtual uint64_t AllocateSpaceForPayload(
const std::string& metadata_filename,
const std::vector<std::string>& key_value_pair_headers,
- brillo::ErrorPtr* error) = 0;
+ Error* error) = 0;
// Wait for merge to complete, then clean up merge after an update has been
// successful.
@@ -127,7 +128,7 @@
// |callback|.
virtual void CleanupSuccessfulUpdate(
std::unique_ptr<CleanupSuccessfulUpdateCallbackInterface> callback,
- brillo::ErrorPtr* error) = 0;
+ Error* error) = 0;
protected:
ServiceDelegateAndroidInterface() = default;
diff --git a/aosp/update_attempter_android.cc b/aosp/update_attempter_android.cc
index 6134885..e52f26a 100644
--- a/aosp/update_attempter_android.cc
+++ b/aosp/update_attempter_android.cc
@@ -81,18 +81,32 @@
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 LogAndSetGenericError(Error* error,
+ int line_number,
+ const char* file_name,
+ const string& reason) {
+ LOG(ERROR) << "Replying with failure: " << file_name << " " << line_number
+ << ": " << reason;
+ error->line_number = line_number;
+ error->file_name = file_name;
+ error->message = reason;
+ error->error_code = ErrorCode::kError;
+ return false;
+}
// 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;
+bool LogAndSetError(Error* error,
+ int line_number,
+ const char* file_name,
+ const string& reason,
+ ErrorCode error_code) {
+ LOG(ERROR) << "Replying with failure: " << file_name << " " << line_number
+ << ": " << reason;
+ error->line_number = line_number;
+ error->file_name = file_name;
+ error->message = reason;
+ error->error_code = error_code;
return false;
}
@@ -105,17 +119,20 @@
bool ParseKeyValuePairHeaders(const vector<string>& key_value_pair_headers,
std::map<string, string>* headers,
- brillo::ErrorPtr* error) {
+ Error* 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);
+ return LogAndSetGenericError(error,
+ __LINE__,
+ __FILE__,
+ "Passed invalid header: " + key_value_pair);
}
if (!headers->emplace(key, value).second)
- return LogAndSetError(error, FROM_HERE, "Passed repeated key: " + key);
+ return LogAndSetGenericError(
+ error, __LINE__, __FILE__, "Passed repeated key: " + key);
}
return true;
}
@@ -131,6 +148,12 @@
: "");
}
+std::string GetCurrentBuildVersion() {
+ // Example: [ro.build.fingerprint]:
+ // [generic/aosp_cf_x86_64_phone/vsoc_x86_64:VanillaIceCream/AOSP.MAIN/user08011303:userdebug/test-keys]
+ return android::base::GetProperty("ro.build.fingerprint", "");
+}
+
} // namespace
UpdateAttempterAndroid::UpdateAttempterAndroid(
@@ -217,14 +240,20 @@
int64_t payload_offset,
int64_t payload_size,
const vector<string>& key_value_pair_headers,
- brillo::ErrorPtr* error) {
+ Error* error) {
if (status_ == UpdateStatus::UPDATED_NEED_REBOOT) {
- return LogAndSetError(
- error, FROM_HERE, "An update already applied, waiting for reboot");
+ return LogAndSetGenericError(
+ error,
+ __LINE__,
+ __FILE__,
+ "An update already applied, waiting for reboot");
}
if (processor_->IsRunning()) {
- return LogAndSetError(
- error, FROM_HERE, "Already processing an update, cancel it first.");
+ return LogAndSetGenericError(
+ error,
+ __LINE__,
+ __FILE__,
+ "Already processing an update, cancel it first.");
}
DCHECK_EQ(status_, UpdateStatus::IDLE);
@@ -307,15 +336,17 @@
if (!headers[kPayloadPropertyNetworkId].empty()) {
if (!base::StringToUint64(headers[kPayloadPropertyNetworkId],
&network_id)) {
- return LogAndSetError(
+ return LogAndSetGenericError(
error,
- FROM_HERE,
+ __LINE__,
+ __FILE__,
"Invalid network_id: " + headers[kPayloadPropertyNetworkId]);
}
if (!network_selector_->SetProcessNetwork(network_id)) {
- return LogAndSetError(
+ return LogAndSetGenericError(
error,
- FROM_HERE,
+ __LINE__,
+ __FILE__,
"Unable to set network_id: " + headers[kPayloadPropertyNetworkId]);
}
}
@@ -379,17 +410,23 @@
int64_t payload_offset,
int64_t payload_size,
const vector<string>& key_value_pair_headers,
- brillo::ErrorPtr* error) {
+ Error* error) {
// update_engine state must be checked before modifying payload_fd_ otherwise
// already running update will be terminated (existing file descriptor will be
// closed)
if (status_ == UpdateStatus::UPDATED_NEED_REBOOT) {
- return LogAndSetError(
- error, FROM_HERE, "An update already applied, waiting for reboot");
+ return LogAndSetGenericError(
+ error,
+ __LINE__,
+ __FILE__,
+ "An update already applied, waiting for reboot");
}
if (processor_->IsRunning()) {
- return LogAndSetError(
- error, FROM_HERE, "Already processing an update, cancel it first.");
+ return LogAndSetGenericError(
+ error,
+ __LINE__,
+ __FILE__,
+ "Already processing an update, cancel it first.");
}
DCHECK_EQ(status_, UpdateStatus::IDLE);
@@ -400,40 +437,48 @@
payload_url, payload_offset, payload_size, key_value_pair_headers, error);
}
-bool UpdateAttempterAndroid::SuspendUpdate(brillo::ErrorPtr* error) {
+bool UpdateAttempterAndroid::SuspendUpdate(Error* error) {
if (!processor_->IsRunning())
- return LogAndSetError(error, FROM_HERE, "No ongoing update to suspend.");
+ return LogAndSetGenericError(
+ error, __LINE__, __FILE__, "No ongoing update to suspend.");
processor_->SuspendProcessing();
return true;
}
-bool UpdateAttempterAndroid::ResumeUpdate(brillo::ErrorPtr* error) {
+bool UpdateAttempterAndroid::ResumeUpdate(Error* error) {
if (!processor_->IsRunning())
- return LogAndSetError(error, FROM_HERE, "No ongoing update to resume.");
+ return LogAndSetGenericError(
+ error, __LINE__, __FILE__, "No ongoing update to resume.");
processor_->ResumeProcessing();
return true;
}
-bool UpdateAttempterAndroid::CancelUpdate(brillo::ErrorPtr* error) {
+bool UpdateAttempterAndroid::CancelUpdate(Error* error) {
if (!processor_->IsRunning())
- return LogAndSetError(error, FROM_HERE, "No ongoing update to cancel.");
+ return LogAndSetGenericError(
+ error, __LINE__, __FILE__, "No ongoing update to cancel.");
processor_->StopProcessing();
return true;
}
-bool UpdateAttempterAndroid::ResetStatus(brillo::ErrorPtr* error) {
+bool UpdateAttempterAndroid::ResetStatus(Error* error) {
LOG(INFO) << "Attempting to reset state from "
<< UpdateStatusToString(status_) << " to UpdateStatus::IDLE";
if (processor_->IsRunning()) {
- return LogAndSetError(
- error, FROM_HERE, "Already processing an update, cancel it first.");
+ return LogAndSetGenericError(
+ error,
+ __LINE__,
+ __FILE__,
+ "Already processing an update, cancel it first.");
}
if (status_ != UpdateStatus::IDLE &&
status_ != UpdateStatus::UPDATED_NEED_REBOOT) {
- return LogAndSetError(error,
- FROM_HERE,
- "Status reset not allowed in this state, please "
- "cancel on going OTA first.");
+ return LogAndSetGenericError(
+ error,
+ __LINE__,
+ __FILE__,
+ "Status reset not allowed in this state, please "
+ "cancel on going OTA first.");
}
if (apex_handler_android_ != nullptr) {
@@ -445,10 +490,11 @@
// after resetting to idle state, it doesn't go back to
// UpdateStatus::UPDATED_NEED_REBOOT state.
if (!ClearUpdateCompletedMarker()) {
- return LogAndSetError(error,
- FROM_HERE,
- "Failed to reset the status because "
- "ClearUpdateCompletedMarker() failed");
+ return LogAndSetGenericError(error,
+ __LINE__,
+ __FILE__,
+ "Failed to reset the status because "
+ "ClearUpdateCompletedMarker() failed");
}
if (status_ == UpdateStatus::UPDATED_NEED_REBOOT) {
if (!resetShouldSwitchSlotOnReboot(error)) {
@@ -478,27 +524,34 @@
const std::string& metadata_filename,
std::string_view expected_metadata_hash,
DeltaArchiveManifest* manifest,
- brillo::ErrorPtr* error) {
+ Error* error) {
FileDescriptorPtr fd(new EintrSafeFileDescriptor);
if (!fd->Open(metadata_filename.c_str(), O_RDONLY)) {
- return LogAndSetError(
- error, FROM_HERE, "Failed to open " + metadata_filename);
+ return LogAndSetError(error,
+ __LINE__,
+ __FILE__,
+ "Failed to open " + metadata_filename,
+ ErrorCode::kDownloadManifestParseError);
}
brillo::Blob metadata(kMaxPayloadHeaderSize);
if (!fd->Read(metadata.data(), metadata.size())) {
return LogAndSetError(
error,
- FROM_HERE,
- "Failed to read payload header from " + metadata_filename);
+ __LINE__,
+ __FILE__,
+ "Failed to read payload header from " + metadata_filename,
+ ErrorCode::kDownloadManifestParseError);
}
ErrorCode errorcode{};
PayloadMetadata payload_metadata;
if (payload_metadata.ParsePayloadHeader(metadata, &errorcode) !=
MetadataParseResult::kSuccess) {
return LogAndSetError(error,
- FROM_HERE,
+ __LINE__,
+ __FILE__,
"Failed to parse payload header: " +
- utils::ErrorCodeToString(errorcode));
+ utils::ErrorCodeToString(errorcode),
+ errorcode);
}
uint64_t metadata_size = payload_metadata.GetMetadataSize() +
payload_metadata.GetMetadataSignatureSize();
@@ -507,16 +560,20 @@
static_cast<uint64_t>(utils::FileSize(metadata_filename))) {
return LogAndSetError(
error,
- FROM_HERE,
- "Invalid metadata size: " + std::to_string(metadata_size));
+ __LINE__,
+ __FILE__,
+ "Invalid metadata size: " + std::to_string(metadata_size),
+ ErrorCode::kDownloadManifestParseError);
}
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);
+ __LINE__,
+ __FILE__,
+ "Failed to read metadata and signature from " + metadata_filename,
+ ErrorCode::kDownloadManifestParseError);
}
fd->Close();
if (!expected_metadata_hash.empty()) {
@@ -525,10 +582,12 @@
metadata.data(), payload_metadata.GetMetadataSize(), &metadata_hash));
if (metadata_hash != expected_metadata_hash) {
return LogAndSetError(error,
- FROM_HERE,
+ __LINE__,
+ __FILE__,
"Metadata hash mismatch. Expected hash: " +
HexEncode(expected_metadata_hash) +
- " actual hash: " + HexEncode(metadata_hash));
+ " actual hash: " + HexEncode(metadata_hash),
+ ErrorCode::kDownloadManifestParseError);
} else {
LOG(INFO) << "Payload metadata hash check passed : "
<< HexEncode(metadata_hash);
@@ -539,27 +598,35 @@
constants::kUpdateCertificatesPath);
if (!payload_verifier) {
return LogAndSetError(error,
- FROM_HERE,
+ __LINE__,
+ __FILE__,
"Failed to create the payload verifier from " +
- std::string(constants::kUpdateCertificatesPath));
+ std::string(constants::kUpdateCertificatesPath),
+ ErrorCode::kDownloadManifestParseError);
}
errorcode = payload_metadata.ValidateMetadataSignature(
metadata, "", *payload_verifier);
if (errorcode != ErrorCode::kSuccess) {
return LogAndSetError(error,
- FROM_HERE,
+ __LINE__,
+ __FILE__,
"Failed to validate metadata signature: " +
- utils::ErrorCodeToString(errorcode));
+ utils::ErrorCodeToString(errorcode),
+ errorcode);
}
if (!payload_metadata.GetManifest(metadata, manifest)) {
- return LogAndSetError(error, FROM_HERE, "Failed to parse manifest.");
+ return LogAndSetError(error,
+ __LINE__,
+ __FILE__,
+ "Failed to parse manifest.",
+ ErrorCode::kDownloadManifestParseError);
}
return true;
}
bool UpdateAttempterAndroid::VerifyPayloadApplicable(
- const std::string& metadata_filename, brillo::ErrorPtr* error) {
+ const std::string& metadata_filename, Error* error) {
DeltaArchiveManifest manifest;
TEST_AND_RETURN_FALSE(
VerifyPayloadParseManifest(metadata_filename, &manifest, error));
@@ -574,14 +641,15 @@
string partition_path;
if (!boot_control_->GetPartitionDevice(
partition.partition_name(), current_slot, &partition_path)) {
- return LogAndSetError(
+ return LogAndSetGenericError(
error,
- FROM_HERE,
+ __LINE__,
+ __FILE__,
"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);
+ return LogAndSetGenericError(
+ error, __LINE__, __FILE__, "Failed to open " + partition_path);
}
for (const InstallOperation& operation : partition.operations()) {
if (!operation.has_src_sha256_hash())
@@ -591,8 +659,8 @@
operation.src_extents(),
manifest.block_size(),
&source_hash)) {
- return LogAndSetError(
- error, FROM_HERE, "Failed to hash " + partition_path);
+ return LogAndSetGenericError(
+ error, __LINE__, __FILE__, "Failed to hash " + partition_path);
}
if (!PartitionWriter::ValidateSourceHash(
source_hash, operation, fd, &errorcode)) {
@@ -964,8 +1032,7 @@
bool UpdateAttempterAndroid::OTARebootSucceeded() const {
const auto current_slot = boot_control_->GetCurrentSlot();
- const string current_version =
- android::base::GetProperty("ro.build.version.incremental", "");
+ const string current_version = GetCurrentBuildVersion();
int64_t previous_slot = -1;
TEST_AND_RETURN_FALSE(prefs_->GetInt64(kPrefsPreviousSlot, &previous_slot));
string previous_version;
@@ -1018,9 +1085,7 @@
}
void UpdateAttempterAndroid::UpdateStateAfterReboot(const OTAResult result) {
- // Example: [ro.build.version.incremental]: [4292972]
- string current_version =
- android::base::GetProperty("ro.build.version.incremental", "");
+ const string current_version = GetCurrentBuildVersion();
TEST_AND_RETURN(!current_version.empty());
// |UpdateStateAfterReboot()| is only called after system reboot, so record
@@ -1122,7 +1187,7 @@
uint64_t UpdateAttempterAndroid::AllocateSpaceForPayload(
const std::string& metadata_filename,
const vector<string>& key_value_pair_headers,
- brillo::ErrorPtr* error) {
+ Error* error) {
std::map<string, string> headers;
if (!ParseKeyValuePairHeaders(key_value_pair_headers, &headers, error)) {
return 0;
@@ -1144,9 +1209,11 @@
if (apex_handler_android_ != nullptr) {
auto result = apex_handler_android_->CalculateSize(apex_infos);
if (!result.ok()) {
- LogAndSetError(error,
- FROM_HERE,
- "Failed to calculate size required for compressed APEX");
+ LogAndSetGenericError(
+ error,
+ __LINE__,
+ __FILE__,
+ "Failed to calculate size required for compressed APEX");
return 0;
}
apex_size_required = *result;
@@ -1154,14 +1221,26 @@
string payload_id = GetPayloadId(headers);
uint64_t required_size = 0;
+ ErrorCode error_code{};
+
if (!DeltaPerformer::PreparePartitionsForUpdate(prefs_,
boot_control_,
GetTargetSlot(),
manifest,
payload_id,
- &required_size)) {
+ &required_size,
+ &error_code)) {
+ if (error_code == ErrorCode::kOverlayfsenabledError) {
+ LogAndSetError(error,
+ __LINE__,
+ __FILE__,
+ "OverlayFS Shouldn't be enabled for OTA.",
+ error_code);
+ return 0;
+ }
if (required_size == 0) {
- LogAndSetError(error, FROM_HERE, "Failed to allocate space for payload.");
+ LogAndSetGenericError(
+ error, __LINE__, __FILE__, "Failed to allocate space for payload.");
return 0;
} else {
LOG(ERROR) << "Insufficient space for payload: " << required_size
@@ -1184,7 +1263,7 @@
void UpdateAttempterAndroid::CleanupSuccessfulUpdate(
std::unique_ptr<CleanupSuccessfulUpdateCallbackInterface> callback,
- brillo::ErrorPtr* error) {
+ Error* error) {
if (cleanup_previous_update_code_.has_value()) {
LOG(INFO) << "CleanupSuccessfulUpdate has previously completed with "
<< utils::ErrorCodeToString(*cleanup_previous_update_code_);
@@ -1197,20 +1276,22 @@
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)));
+ callback_ptr->RegisterForDeathNotifications([this, callback_ptr]() {
+ RemoveCleanupPreviousUpdateCallback(callback_ptr);
+ });
}
ScheduleCleanupPreviousUpdate();
}
bool UpdateAttempterAndroid::setShouldSwitchSlotOnReboot(
- const std::string& metadata_filename, brillo::ErrorPtr* error) {
+ const std::string& metadata_filename, Error* error) {
LOG(INFO) << "setShouldSwitchSlotOnReboot(" << metadata_filename << ")";
if (processor_->IsRunning()) {
- return LogAndSetError(
- error, FROM_HERE, "Already processing an update, cancel it first.");
+ return LogAndSetGenericError(
+ error,
+ __LINE__,
+ __FILE__,
+ "Already processing an update, cancel it first.");
}
DeltaArchiveManifest manifest;
TEST_AND_RETURN_FALSE(
@@ -1233,6 +1314,7 @@
std::make_unique<PostinstallRunnerAction>(boot_control_, hardware_);
SetStatusAndNotify(UpdateStatus::VERIFYING);
postinstall_runner_action->set_delegate(this);
+ ErrorCode error_code{};
// If last error code is kUpdatedButNotActive, we know that we reached this
// state by calling applyPayload() with switch_slot=false. That applyPayload()
@@ -1247,19 +1329,21 @@
GetTargetSlot(),
manifest,
false /* should update */,
- nullptr)) {
- return LogAndSetError(
- error, FROM_HERE, "Failed to PreparePartitionsForUpdate");
+ nullptr,
+ &error_code)) {
+ return LogAndSetGenericError(
+ error, __LINE__, __FILE__, "Failed to PreparePartitionsForUpdate");
}
- ErrorCode error_code{};
if (!install_plan_.ParsePartitions(manifest.partitions(),
boot_control_,
manifest.block_size(),
&error_code)) {
return LogAndSetError(error,
- FROM_HERE,
+ __LINE__,
+ __FILE__,
"Failed to LoadPartitionsFromSlots " +
- utils::ErrorCodeToString(error_code));
+ utils::ErrorCodeToString(error_code),
+ error_code);
}
auto filesystem_verifier_action =
@@ -1278,24 +1362,27 @@
return true;
}
-bool UpdateAttempterAndroid::resetShouldSwitchSlotOnReboot(
- brillo::ErrorPtr* error) {
+bool UpdateAttempterAndroid::resetShouldSwitchSlotOnReboot(Error* error) {
if (processor_->IsRunning()) {
- return LogAndSetError(
- error, FROM_HERE, "Already processing an update, cancel it first.");
+ return LogAndSetGenericError(
+ error,
+ __LINE__,
+ __FILE__,
+ "Already processing an update, cancel it first.");
}
TEST_AND_RETURN_FALSE(ClearUpdateCompletedMarker());
// Update the boot flags so the current slot has higher priority.
if (!boot_control_->SetActiveBootSlot(GetCurrentSlot())) {
- return LogAndSetError(error, FROM_HERE, "Failed to SetActiveBootSlot");
+ return LogAndSetGenericError(
+ error, __LINE__, __FILE__, "Failed to SetActiveBootSlot");
}
// 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) {}))) {
- return LogAndSetError(
- error, FROM_HERE, "Failed to MarkBootSuccessfulAsync");
+ return LogAndSetGenericError(
+ error, __LINE__, __FILE__, "Failed to MarkBootSuccessfulAsync");
}
// Resets the warm reset property since we won't switch the slot.
diff --git a/aosp/update_attempter_android.h b/aosp/update_attempter_android.h
index bbffbe9..b7851f1 100644
--- a/aosp/update_attempter_android.h
+++ b/aosp/update_attempter_android.h
@@ -77,28 +77,28 @@
int64_t payload_offset,
int64_t payload_size,
const std::vector<std::string>& key_value_pair_headers,
- brillo::ErrorPtr* error) override;
+ Error* 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;
+ Error* error) override;
+ bool SuspendUpdate(Error* error) override;
+ bool ResumeUpdate(Error* error) override;
+ bool CancelUpdate(Error* error) override;
+ bool ResetStatus(Error* error) override;
bool VerifyPayloadApplicable(const std::string& metadata_filename,
- brillo::ErrorPtr* error) override;
+ Error* error) override;
uint64_t AllocateSpaceForPayload(
const std::string& metadata_filename,
const std::vector<std::string>& key_value_pair_headers,
- brillo::ErrorPtr* error) override;
+ Error* error) override;
void CleanupSuccessfulUpdate(
std::unique_ptr<CleanupSuccessfulUpdateCallbackInterface> callback,
- brillo::ErrorPtr* error) override;
+ Error* error) override;
bool setShouldSwitchSlotOnReboot(const std::string& metadata_filename,
- brillo::ErrorPtr* error) override;
- bool resetShouldSwitchSlotOnReboot(brillo::ErrorPtr* error) override;
+ Error* error) override;
+ bool resetShouldSwitchSlotOnReboot(Error* error) override;
// ActionProcessorDelegate methods:
void ProcessingDone(const ActionProcessor* processor,
@@ -223,10 +223,10 @@
static bool VerifyPayloadParseManifest(const std::string& metadata_filename,
std::string_view metadata_hash,
DeltaArchiveManifest* manifest,
- brillo::ErrorPtr* error);
+ Error* error);
static bool VerifyPayloadParseManifest(const std::string& metadata_filename,
DeltaArchiveManifest* manifest,
- brillo::ErrorPtr* error) {
+ Error* error) {
return VerifyPayloadParseManifest(metadata_filename, "", manifest, error);
}
diff --git a/aosp/update_attempter_android_integration_test.cc b/aosp/update_attempter_android_integration_test.cc
index 909aa3c..8f3bad7 100644
--- a/aosp/update_attempter_android_integration_test.cc
+++ b/aosp/update_attempter_android_integration_test.cc
@@ -36,6 +36,7 @@
#include "update_engine/aosp/daemon_state_android.h"
#include "update_engine/aosp/update_attempter_android.h"
#include "update_engine/common/constants.h"
+#include "update_engine/common/error.h"
#include "update_engine/common/fake_boot_control.h"
#include "update_engine/common/fake_hardware.h"
#include "update_engine/common/hash_calculator.h"
@@ -299,7 +300,7 @@
&metadata_size));
LOG(INFO) << "Signature offset: " << manifest->signatures_offset()
<< ", Signature size: " << manifest->signatures_size();
- brillo::ErrorPtr error;
+ Error error;
HashCalculator::RawHashOfFile(payload_file_.path(), &hash);
daemon_state_.AddObserver(this);
ASSERT_TRUE(update_attempter_android_.ApplyPayload(
@@ -311,10 +312,10 @@
("=" + brillo::data_encoding::Base64Encode(hash))},
&error));
brillo::MessageLoop::current()->Run();
- if (error) {
- LOG(ERROR) << error->GetMessage();
+ if (error.error_code != ErrorCode::kSuccess) {
+ LOG(ERROR) << error.message;
}
- ASSERT_EQ(error, nullptr);
+ ASSERT_EQ(error.error_code, ErrorCode::kSuccess) << error.message;
ASSERT_EQ(completion_code_, ErrorCode::kSuccess);
}
diff --git a/aosp/update_engine_client_android.cc b/aosp/update_engine_client_android.cc
index c00e9f5..81736a7 100644
--- a/aosp/update_engine_client_android.cc
+++ b/aosp/update_engine_client_android.cc
@@ -117,8 +117,7 @@
android::BinderWrapper::Create();
android::BinderWrapper::Get()->RegisterForDeathNotifications(
android::os::IUpdateEngine::asBinder(service_),
- base::Bind(&UpdateEngineClientAndroid::UpdateEngineServiceDied,
- base::Unretained(this)));
+ [this]() { UpdateEngineServiceDied(); });
}
int UpdateEngineClientAndroid::OnInit() {
@@ -207,7 +206,7 @@
if (FLAGS_follow) {
// Register a callback object with the service.
callback_ = new UECallback(this);
- bool bound;
+ bool bound = false;
if (!service_->bind(callback_, &bound).isOk() || !bound) {
LOG(ERROR) << "Failed to bind() the UpdateEngine daemon.";
return 1;
diff --git a/common/action_processor.h b/common/action_processor.h
index 6108c3e..5a4286f 100644
--- a/common/action_processor.h
+++ b/common/action_processor.h
@@ -22,7 +22,6 @@
#include <vector>
#include <base/macros.h>
-#include <brillo/errors/error.h>
#include "update_engine/common/error_code.h"
diff --git a/common/dynamic_partition_control_interface.h b/common/dynamic_partition_control_interface.h
index 490892e..5c0a03b 100644
--- a/common/dynamic_partition_control_interface.h
+++ b/common/dynamic_partition_control_interface.h
@@ -106,7 +106,8 @@
uint32_t target_slot,
const DeltaArchiveManifest& manifest,
bool update,
- uint64_t* required_size) = 0;
+ uint64_t* required_size,
+ ErrorCode* error = nullptr) = 0;
// After writing to new partitions, before rebooting into the new slot, call
// this function to indicate writes to new partitions are done.
diff --git a/common/dynamic_partition_control_stub.cc b/common/dynamic_partition_control_stub.cc
index fd9a3b4..7d681cf 100644
--- a/common/dynamic_partition_control_stub.cc
+++ b/common/dynamic_partition_control_stub.cc
@@ -62,7 +62,8 @@
uint32_t target_slot,
const DeltaArchiveManifest& manifest,
bool update,
- uint64_t* required_size) {
+ uint64_t* required_size,
+ ErrorCode* error) {
return true;
}
diff --git a/common/dynamic_partition_control_stub.h b/common/dynamic_partition_control_stub.h
index 1db6a78..610a82a 100644
--- a/common/dynamic_partition_control_stub.h
+++ b/common/dynamic_partition_control_stub.h
@@ -43,7 +43,8 @@
uint32_t target_slot,
const DeltaArchiveManifest& manifest,
bool update,
- uint64_t* required_size) override;
+ uint64_t* required_size,
+ ErrorCode* error = nullptr) override;
bool FinishUpdate(bool powerwash_required) override;
std::unique_ptr<AbstractAction> GetCleanupPreviousUpdateAction(
diff --git a/common/error.h b/common/error.h
new file mode 100644
index 0000000..ab38e64
--- /dev/null
+++ b/common/error.h
@@ -0,0 +1,29 @@
+// Copyright (C) 2023 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.
+//
+#pragma once
+
+#include "update_engine/common/error_code.h"
+#include <string>
+
+namespace chromeos_update_engine {
+
+struct Error {
+ ErrorCode error_code;
+ std::string message;
+ int line_number;
+ const char* file_name;
+};
+
+} // namespace chromeos_update_engine
diff --git a/common/error_code.h b/common/error_code.h
index a889888..7924579 100644
--- a/common/error_code.h
+++ b/common/error_code.h
@@ -87,6 +87,7 @@
kDeviceCorrupted = 61,
kPackageExcludedFromUpdate = 62,
kPostInstallMountError = 63,
+ kOverlayfsenabledError = 64,
// VERY IMPORTANT! When adding new error codes:
//
diff --git a/common/error_code_utils.cc b/common/error_code_utils.cc
index 421544a..12a98bf 100644
--- a/common/error_code_utils.cc
+++ b/common/error_code_utils.cc
@@ -175,6 +175,8 @@
return "ErrorCode::kPackageExcludedFromUpdate";
case ErrorCode::kPostInstallMountError:
return "ErrorCode::kPostInstallMountError";
+ case ErrorCode::kOverlayfsenabledError:
+ return "ErrorCode::kOverlayfsenabledError";
// Don't add a default case to let the compiler warn about newly added
// error codes which should be added here.
}
diff --git a/common/mock_dynamic_partition_control.h b/common/mock_dynamic_partition_control.h
index 79909b4..968246a 100644
--- a/common/mock_dynamic_partition_control.h
+++ b/common/mock_dynamic_partition_control.h
@@ -66,11 +66,15 @@
std::optional<uint64_t> label),
(override));
- MOCK_METHOD(
- bool,
- PreparePartitionsForUpdate,
- (uint32_t, uint32_t, const DeltaArchiveManifest&, bool, uint64_t*),
- (override));
+ MOCK_METHOD(bool,
+ PreparePartitionsForUpdate,
+ (uint32_t,
+ uint32_t,
+ const DeltaArchiveManifest&,
+ bool,
+ uint64_t*,
+ ErrorCode*),
+ (override));
MOCK_METHOD(bool, ResetUpdate, (PrefsInterface*), (override));
MOCK_METHOD(std::unique_ptr<AbstractAction>,
diff --git a/libbinderwrapper/Android.bp b/libbinderwrapper/Android.bp
new file mode 100644
index 0000000..87c55fa
--- /dev/null
+++ b/libbinderwrapper/Android.bp
@@ -0,0 +1,63 @@
+//
+// 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.
+//
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_defaults {
+ name: "libbinderwrapper_defaults",
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-unused-parameter",
+
+ ],
+ export_include_dirs: ["include"],
+ shared_libs: [
+ "libbinder",
+ "libutils",
+ ],
+}
+
+// libbinderwrapper shared library
+// ========================================================
+cc_library_shared {
+ name: "libbinderwrapper",
+ defaults: ["libbinderwrapper_defaults"],
+ vendor_available: true,
+
+ srcs: [
+ "binder_wrapper.cc",
+ "real_binder_wrapper.cc",
+ ],
+}
+
+// libbinderwrapper_test_support static library
+// ========================================================
+cc_library_static {
+ name: "libbinderwrapper_test_support",
+ defaults: ["libbinderwrapper_defaults"],
+
+ static_libs: ["libgtest"],
+ shared_libs: ["libbinderwrapper"],
+
+ srcs: [
+ "binder_test_base.cc",
+ "stub_binder_wrapper.cc",
+ ],
+}
diff --git a/libbinderwrapper/binder_test_base.cc b/libbinderwrapper/binder_test_base.cc
new file mode 100644
index 0000000..af93a04
--- /dev/null
+++ b/libbinderwrapper/binder_test_base.cc
@@ -0,0 +1,33 @@
+/*
+ * 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 <binderwrapper/binder_test_base.h>
+
+#include <binderwrapper/binder_wrapper.h>
+#include <binderwrapper/stub_binder_wrapper.h>
+
+namespace android {
+
+BinderTestBase::BinderTestBase() : binder_wrapper_(new StubBinderWrapper()) {
+ // Pass ownership.
+ BinderWrapper::InitForTesting(binder_wrapper_);
+}
+
+BinderTestBase::~BinderTestBase() {
+ BinderWrapper::Destroy();
+}
+
+} // namespace android
diff --git a/libbinderwrapper/binder_wrapper.cc b/libbinderwrapper/binder_wrapper.cc
new file mode 100644
index 0000000..f5e7476
--- /dev/null
+++ b/libbinderwrapper/binder_wrapper.cc
@@ -0,0 +1,60 @@
+/*
+ * 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 <binderwrapper/binder_wrapper.h>
+
+#include <android-base/logging.h>
+
+#include "real_binder_wrapper.h"
+
+namespace android {
+
+// Singleton instance.
+BinderWrapper* BinderWrapper::instance_ = nullptr;
+
+// static
+void BinderWrapper::Create() {
+ CHECK(!instance_) << "Already initialized; missing call to Destroy()?";
+ instance_ = new RealBinderWrapper();
+}
+
+// static
+void BinderWrapper::InitForTesting(BinderWrapper* wrapper) {
+ CHECK(!instance_) << "Already initialized; missing call to Destroy()?";
+ instance_ = wrapper;
+}
+
+// static
+void BinderWrapper::Destroy() {
+ CHECK(instance_) << "Not initialized; missing call to Create()?";
+ delete instance_;
+ instance_ = nullptr;
+}
+
+// static
+BinderWrapper* BinderWrapper::Get() {
+ CHECK(instance_) << "Not initialized; missing call to Create()?";
+ return instance_;
+}
+
+// static
+BinderWrapper* BinderWrapper::GetOrCreateInstance() {
+ if (!instance_)
+ instance_ = new RealBinderWrapper();
+ return instance_;
+}
+
+} // namespace android
diff --git a/libbinderwrapper/include/binderwrapper/binder_test_base.h b/libbinderwrapper/include/binderwrapper/binder_test_base.h
new file mode 100644
index 0000000..46eca3f
--- /dev/null
+++ b/libbinderwrapper/include/binderwrapper/binder_test_base.h
@@ -0,0 +1,44 @@
+/*
+ * 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 SYSTEM_CORE_INCLUDE_BINDERWRAPPER_BINDER_TEST_BASE_H_
+#define SYSTEM_CORE_INCLUDE_BINDERWRAPPER_BINDER_TEST_BASE_H_
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+class StubBinderWrapper;
+
+// Class that can be inherited from (or aliased via typedef/using) when writing
+// tests that use StubBinderManager.
+class BinderTestBase : public ::testing::Test {
+ public:
+ BinderTestBase();
+ ~BinderTestBase() override;
+
+ StubBinderWrapper* binder_wrapper() { return binder_wrapper_; }
+
+ protected:
+ StubBinderWrapper* binder_wrapper_; // Not owned.
+
+ private:
+ BinderTestBase(const BinderTestBase&) = delete;
+};
+
+} // namespace android
+
+#endif // SYSTEM_CORE_INCLUDE_BINDERWRAPPER_BINDER_TEST_BASE_H_
diff --git a/libbinderwrapper/include/binderwrapper/binder_wrapper.h b/libbinderwrapper/include/binderwrapper/binder_wrapper.h
new file mode 100644
index 0000000..fc5708b
--- /dev/null
+++ b/libbinderwrapper/include/binderwrapper/binder_wrapper.h
@@ -0,0 +1,87 @@
+/*
+ * 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 SYSTEM_CORE_INCLUDE_BINDERWRAPPER_BINDER_WRAPPER_H_
+#define SYSTEM_CORE_INCLUDE_BINDERWRAPPER_BINDER_WRAPPER_H_
+
+#include <sys/types.h>
+
+#include <functional>
+#include <string>
+
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+class BBinder;
+class IBinder;
+
+// Wraps libbinder to make it testable.
+// NOTE: Static methods of this class are not thread-safe.
+class BinderWrapper {
+ public:
+ virtual ~BinderWrapper() {}
+
+ // Creates and initializes the singleton (using a wrapper that communicates
+ // with the real binder system).
+ static void Create();
+
+ // Initializes |wrapper| as the singleton, taking ownership of it. Tests that
+ // want to inject their own wrappers should call this instead of Create().
+ static void InitForTesting(BinderWrapper* wrapper);
+
+ // Destroys the singleton. Must be called before calling Create() or
+ // InitForTesting() a second time.
+ static void Destroy();
+
+ // Returns the singleton instance previously created by Create() or set by
+ // InitForTesting().
+ static BinderWrapper* Get();
+
+ // Returns the singleton instance if it was previously created by Create() or
+ // set by InitForTesting(), or creates a new one by calling Create().
+ static BinderWrapper* GetOrCreateInstance();
+
+ // Gets the binder for communicating with the service identified by
+ // |service_name|, returning null immediately if it doesn't exist.
+ virtual sp<IBinder> GetService(const std::string& service_name) = 0;
+
+ // Registers |binder| as |service_name| with the service manager.
+ virtual bool RegisterService(const std::string& service_name,
+ const sp<IBinder>& binder) = 0;
+
+ // Creates a local binder object.
+ virtual sp<BBinder> CreateLocalBinder() = 0;
+
+ // Registers |callback| to be invoked when |binder| dies. If another callback
+ // is currently registered for |binder|, it will be replaced.
+ virtual bool RegisterForDeathNotifications(const sp<IBinder>& binder,
+ const std::function<void()>&) = 0;
+
+ // Unregisters the callback, if any, for |binder|.
+ virtual bool UnregisterForDeathNotifications(const sp<IBinder>& binder) = 0;
+
+ // When called while in a transaction, returns the caller's UID or PID.
+ virtual uid_t GetCallingUid() = 0;
+ virtual pid_t GetCallingPid() = 0;
+
+ private:
+ static BinderWrapper* instance_;
+};
+
+} // namespace android
+
+#endif // SYSTEM_CORE_INCLUDE_BINDERWRAPPER_BINDER_WRAPPER_H_
diff --git a/libbinderwrapper/include/binderwrapper/stub_binder_wrapper.h b/libbinderwrapper/include/binderwrapper/stub_binder_wrapper.h
new file mode 100644
index 0000000..2a7f77e
--- /dev/null
+++ b/libbinderwrapper/include/binderwrapper/stub_binder_wrapper.h
@@ -0,0 +1,132 @@
+/*
+ * 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 SYSTEM_CORE_INCLUDE_BINDERWRAPPER_STUB_BINDER_WRAPPER_H_
+#define SYSTEM_CORE_INCLUDE_BINDERWRAPPER_STUB_BINDER_WRAPPER_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include <binder/Binder.h>
+#include <binder/IBinder.h>
+#include <binderwrapper/binder_wrapper.h>
+
+namespace android {
+
+// Stub implementation of BinderWrapper for testing.
+//
+// Example usage:
+//
+// First, assuming a base IFoo binder interface, create a stub class that
+// derives from BnFoo to implement the receiver side of the communication:
+//
+// class StubFoo : public BnFoo {
+// public:
+// ...
+// status_t doSomething(int arg) override {
+// // e.g. save passed-in value for later inspection by tests.
+// return OK;
+// }
+// };
+//
+// Next, from your test code, inject a StubBinderManager either directly or by
+// inheriting from the BinderTestBase class:
+//
+// StubBinderWrapper* wrapper = new StubBinderWrapper();
+// BinderWrapper::InitForTesting(wrapper); // Takes ownership.
+//
+// Also from your test, create a StubFoo and register it with the wrapper:
+//
+// StubFoo* foo = new StubFoo();
+// sp<IBinder> binder(foo);
+// wrapper->SetBinderForService("foo", binder);
+//
+// The code being tested can now use the wrapper to get the stub and call it:
+//
+// sp<IBinder> binder = BinderWrapper::Get()->GetService("foo");
+// CHECK(binder.get());
+// sp<IFoo> foo = interface_cast<IFoo>(binder);
+// CHECK_EQ(foo->doSomething(3), OK);
+//
+// To create a local BBinder object, production code can call
+// CreateLocalBinder(). Then, a test can get the BBinder's address via
+// local_binders() to check that they're passed as expected in binder calls.
+//
+class StubBinderWrapper : public BinderWrapper {
+ public:
+ StubBinderWrapper();
+ ~StubBinderWrapper() override;
+
+ const std::vector<sp<BBinder>>& local_binders() const {
+ return local_binders_;
+ }
+ void clear_local_binders() { local_binders_.clear(); }
+
+ void set_calling_uid(uid_t uid) { calling_uid_ = uid; }
+ void set_calling_pid(pid_t pid) { calling_pid_ = pid; }
+
+ // Sets the binder to return when |service_name| is passed to GetService() or
+ // WaitForService().
+ void SetBinderForService(const std::string& service_name,
+ const sp<IBinder>& binder);
+
+ // Returns the binder previously registered for |service_name| via
+ // RegisterService(), or null if the service hasn't been registered.
+ sp<IBinder> GetRegisteredService(const std::string& service_name) const;
+
+ // Run the calback in |death_callbacks_| corresponding to |binder|.
+ void NotifyAboutBinderDeath(const sp<IBinder>& binder);
+
+ // BinderWrapper:
+ sp<IBinder> GetService(const std::string& service_name) override;
+ bool RegisterService(const std::string& service_name,
+ const sp<IBinder>& binder) override;
+ sp<BBinder> CreateLocalBinder() override;
+ bool RegisterForDeathNotifications(const sp<IBinder>& binder,
+ const std::function<void()>& callback) override;
+ bool UnregisterForDeathNotifications(const sp<IBinder>& binder) override;
+ uid_t GetCallingUid() override;
+ pid_t GetCallingPid() override;
+
+ private:
+ using ServiceMap = std::map<std::string, sp<IBinder>>;
+
+ // Map from service name to associated binder handle. Used by GetService() and
+ // WaitForService().
+ ServiceMap services_to_return_;
+
+ // Map from service name to associated binder handle. Updated by
+ // RegisterService().
+ ServiceMap registered_services_;
+
+ // Local binders returned by CreateLocalBinder().
+ std::vector<sp<BBinder>> local_binders_;
+
+ // Map from binder handle to the callback that should be invoked on binder
+ // death.
+ std::map<sp<IBinder>, std::function<void()>> death_callbacks_;
+
+ // Values to return from GetCallingUid() and GetCallingPid();
+ uid_t calling_uid_;
+ pid_t calling_pid_;
+
+ StubBinderWrapper(const StubBinderWrapper&) = delete;
+};
+
+} // namespace android
+
+#endif // SYSTEM_CORE_INCLUDE_BINDERWRAPPER_STUB_BINDER_WRAPPER_H_
diff --git a/libbinderwrapper/real_binder_wrapper.cc b/libbinderwrapper/real_binder_wrapper.cc
new file mode 100644
index 0000000..d214ce3
--- /dev/null
+++ b/libbinderwrapper/real_binder_wrapper.cc
@@ -0,0 +1,121 @@
+/*
+ * 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 "real_binder_wrapper.h"
+
+#include <android-base/logging.h>
+
+#include <binder/Binder.h>
+#include <binder/IBinder.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+
+namespace android {
+
+// Class that handles binder death notifications. libbinder wants the recipient
+// to be wrapped in sp<>, so registering RealBinderWrapper as a recipient would
+// be awkward.
+class RealBinderWrapper::DeathRecipient : public IBinder::DeathRecipient {
+ public:
+ explicit DeathRecipient(const std::function<void()>& callback)
+ : callback_(std::move(callback)) {}
+ ~DeathRecipient() = default;
+
+ // IBinder::DeathRecipient:
+ void binderDied(const wp<IBinder>& who) override { callback_(); }
+
+ private:
+ // Callback to run in response to binder death.
+ std::function<void()> callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(DeathRecipient);
+};
+
+RealBinderWrapper::RealBinderWrapper() = default;
+
+RealBinderWrapper::~RealBinderWrapper() = default;
+
+sp<IBinder> RealBinderWrapper::GetService(const std::string& service_name) {
+ sp<IServiceManager> service_manager = defaultServiceManager();
+ if (!service_manager.get()) {
+ LOG(ERROR) << "Unable to get service manager";
+ return sp<IBinder>();
+ }
+ sp<IBinder> binder =
+ service_manager->checkService(String16(service_name.c_str()));
+ if (!binder.get())
+ LOG(ERROR) << "Unable to get \"" << service_name << "\" service";
+ return binder;
+}
+
+bool RealBinderWrapper::RegisterService(const std::string& service_name,
+ const sp<IBinder>& binder) {
+ sp<IServiceManager> service_manager = defaultServiceManager();
+ if (!service_manager.get()) {
+ LOG(ERROR) << "Unable to get service manager";
+ return false;
+ }
+ status_t status = defaultServiceManager()->addService(
+ String16(service_name.c_str()), binder);
+ if (status != OK) {
+ LOG(ERROR) << "Failed to register \"" << service_name << "\" with service "
+ << "manager";
+ return false;
+ }
+ return true;
+}
+
+sp<BBinder> RealBinderWrapper::CreateLocalBinder() {
+ return sp<BBinder>(new BBinder());
+}
+
+bool RealBinderWrapper::RegisterForDeathNotifications(const sp<IBinder>& binder,
+ const std::function<void()>& callback) {
+ sp<DeathRecipient> recipient(new DeathRecipient(callback));
+ if (binder->linkToDeath(recipient) != OK) {
+ LOG(ERROR) << "Failed to register for death notifications on "
+ << binder.get();
+ return false;
+ }
+ death_recipients_[binder] = recipient;
+ return true;
+}
+
+bool RealBinderWrapper::UnregisterForDeathNotifications(
+ const sp<IBinder>& binder) {
+ auto it = death_recipients_.find(binder);
+ if (it == death_recipients_.end()) {
+ LOG(ERROR) << "Not registered for death notifications on " << binder.get();
+ return false;
+ }
+ if (binder->unlinkToDeath(it->second) != OK) {
+ LOG(ERROR) << "Failed to unregister for death notifications on "
+ << binder.get();
+ return false;
+ }
+ death_recipients_.erase(it);
+ return true;
+}
+
+uid_t RealBinderWrapper::GetCallingUid() {
+ return IPCThreadState::self()->getCallingUid();
+}
+
+pid_t RealBinderWrapper::GetCallingPid() {
+ return IPCThreadState::self()->getCallingPid();
+}
+
+} // namespace android
diff --git a/libbinderwrapper/real_binder_wrapper.h b/libbinderwrapper/real_binder_wrapper.h
new file mode 100644
index 0000000..d0468f0
--- /dev/null
+++ b/libbinderwrapper/real_binder_wrapper.h
@@ -0,0 +1,57 @@
+/*
+ * 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 SYSTEM_CORE_LIBBINDERWRAPPER_REAL_BINDER_WRAPPER_H_
+#define SYSTEM_CORE_LIBBINDERWRAPPER_REAL_BINDER_WRAPPER_H_
+
+#include <map>
+
+#include <binderwrapper/binder_wrapper.h>
+
+namespace android {
+
+class IBinder;
+
+// Real implementation of BinderWrapper.
+class RealBinderWrapper : public BinderWrapper {
+ public:
+ RealBinderWrapper();
+ ~RealBinderWrapper() override;
+
+ // BinderWrapper:
+ sp<IBinder> GetService(const std::string& service_name) override;
+ bool RegisterService(const std::string& service_name,
+ const sp<IBinder>& binder) override;
+ sp<BBinder> CreateLocalBinder() override;
+ bool RegisterForDeathNotifications(const sp<IBinder>& binder,
+ const std::function<void()>& callback) override;
+ bool UnregisterForDeathNotifications(const sp<IBinder>& binder) override;
+ uid_t GetCallingUid() override;
+ pid_t GetCallingPid() override;
+
+ private:
+ class DeathRecipient;
+
+ // Map from binder handle to object that should be notified of the binder's
+ // death.
+ std::map<sp<IBinder>, sp<DeathRecipient>> death_recipients_;
+
+ RealBinderWrapper(const RealBinderWrapper&) = delete;
+};
+
+} // namespace android
+
+#endif // SYSTEM_CORE_LIBBINDER_WRAPPER_REAL_BINDER_WRAPPER_H_
diff --git a/libbinderwrapper/stub_binder_wrapper.cc b/libbinderwrapper/stub_binder_wrapper.cc
new file mode 100644
index 0000000..fabf122
--- /dev/null
+++ b/libbinderwrapper/stub_binder_wrapper.cc
@@ -0,0 +1,85 @@
+/*
+ * 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 <binderwrapper/stub_binder_wrapper.h>
+
+#include <android-base/logging.h>
+
+#include <binder/Binder.h>
+#include <binder/IBinder.h>
+
+namespace android {
+
+StubBinderWrapper::StubBinderWrapper()
+ : calling_uid_(-1),
+ calling_pid_(-1) {}
+
+StubBinderWrapper::~StubBinderWrapper() = default;
+
+void StubBinderWrapper::SetBinderForService(const std::string& service_name,
+ const sp<IBinder>& binder) {
+ services_to_return_[service_name] = binder;
+}
+
+sp<IBinder> StubBinderWrapper::GetRegisteredService(
+ const std::string& service_name) const {
+ const auto it = registered_services_.find(service_name);
+ return it != registered_services_.end() ? it->second : sp<IBinder>();
+}
+
+void StubBinderWrapper::NotifyAboutBinderDeath(const sp<IBinder>& binder) {
+ const auto it = death_callbacks_.find(binder);
+ if (it != death_callbacks_.end()) it->second();
+}
+
+sp<IBinder> StubBinderWrapper::GetService(const std::string& service_name) {
+ const auto it = services_to_return_.find(service_name);
+ return it != services_to_return_.end() ? it->second : sp<IBinder>();
+}
+
+bool StubBinderWrapper::RegisterService(const std::string& service_name,
+ const sp<IBinder>& binder) {
+ registered_services_[service_name] = binder;
+ return true;
+}
+
+sp<BBinder> StubBinderWrapper::CreateLocalBinder() {
+ sp<BBinder> binder(new BBinder());
+ local_binders_.push_back(binder);
+ return binder;
+}
+
+bool StubBinderWrapper::RegisterForDeathNotifications(const sp<IBinder>& binder,
+ const std::function<void()>& callback) {
+ death_callbacks_[binder] = callback;
+ return true;
+}
+
+bool StubBinderWrapper::UnregisterForDeathNotifications(
+ const sp<IBinder>& binder) {
+ death_callbacks_.erase(binder);
+ return true;
+}
+
+uid_t StubBinderWrapper::GetCallingUid() {
+ return calling_uid_;
+}
+
+pid_t StubBinderWrapper::GetCallingPid() {
+ return calling_pid_;
+}
+
+} // namespace android
diff --git a/metrics_utils.cc b/metrics_utils.cc
index ec35fe2..94a0520 100644
--- a/metrics_utils.cc
+++ b/metrics_utils.cc
@@ -73,6 +73,7 @@
case ErrorCode::kVerityCalculationError:
case ErrorCode::kNotEnoughSpace:
case ErrorCode::kDeviceCorrupted:
+ case ErrorCode::kOverlayfsenabledError:
return metrics::AttemptResult::kOperationExecutionError;
case ErrorCode::kDownloadMetadataSignatureMismatch:
@@ -190,6 +191,7 @@
case ErrorCode::kFilesystemCopierError:
case ErrorCode::kPostinstallRunnerError:
case ErrorCode::kPostInstallMountError:
+ case ErrorCode::kOverlayfsenabledError:
case ErrorCode::kPayloadMismatchedType:
case ErrorCode::kInstallDeviceOpenError:
case ErrorCode::kKernelDeviceOpenError:
@@ -367,8 +369,8 @@
TimeDelta time_to_reboot = current_time - system_updated_at;
if (time_to_reboot.ToInternalValue() < 0) {
LOG(WARNING) << "time_to_reboot is negative - system_updated_at: "
- << utils::ToString(system_updated_at) << " current time: "
- << utils::ToString(current_time);
+ << utils::ToString(system_updated_at)
+ << " current time: " << utils::ToString(current_time);
return false;
}
metrics_reporter->ReportTimeToReboot(time_to_reboot.InMinutes());
diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc
index 298bde1..267805e 100644
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -440,6 +440,9 @@
// were written, or false on any error, regardless of progress
// and stores an action exit code in |error|.
bool DeltaPerformer::Write(const void* bytes, size_t count, ErrorCode* error) {
+ if (!error) {
+ return false;
+ }
*error = ErrorCode::kSuccess;
const char* c_bytes = reinterpret_cast<const char*>(bytes);
@@ -730,8 +733,10 @@
// slot suffix of the partitions in the metadata.
if (install_plan_->target_slot != BootControlInterface::kInvalidSlot) {
uint64_t required_size = 0;
- if (!PreparePartitionsForUpdate(&required_size)) {
- if (required_size > 0) {
+ if (!PreparePartitionsForUpdate(&required_size, error)) {
+ if (*error == ErrorCode::kOverlayfsenabledError) {
+ return false;
+ } else if (required_size > 0) {
*error = ErrorCode::kNotEnoughSpace;
} else {
*error = ErrorCode::kInstallDeviceOpenError;
@@ -802,7 +807,8 @@
return true;
}
-bool DeltaPerformer::PreparePartitionsForUpdate(uint64_t* required_size) {
+bool DeltaPerformer::PreparePartitionsForUpdate(uint64_t* required_size,
+ ErrorCode* error) {
// Call static PreparePartitionsForUpdate with hash from
// kPrefsUpdateCheckResponseHash to ensure hash of payload that space is
// preallocated for is the same as the hash of payload being applied.
@@ -814,7 +820,8 @@
install_plan_->target_slot,
manifest_,
update_check_response_hash,
- required_size);
+ required_size,
+ error);
}
bool DeltaPerformer::PreparePartitionsForUpdate(
@@ -823,7 +830,8 @@
BootControlInterface::Slot target_slot,
const DeltaArchiveManifest& manifest,
const std::string& update_check_response_hash,
- uint64_t* required_size) {
+ uint64_t* required_size,
+ ErrorCode* error) {
string last_hash;
ignore_result(
prefs->GetString(kPrefsDynamicPartitionMetadataUpdated, &last_hash));
@@ -845,9 +853,11 @@
target_slot,
manifest,
!is_resume /* should update */,
- required_size)) {
+ required_size,
+ error)) {
LOG(ERROR) << "Unable to initialize partition metadata for slot "
- << BootControlInterface::SlotName(target_slot);
+ << BootControlInterface::SlotName(target_slot) << " "
+ << utils::ErrorCodeToString(*error);
return false;
}
diff --git a/payload_consumer/delta_performer.h b/payload_consumer/delta_performer.h
index 2616b6e..e83e000 100644
--- a/payload_consumer/delta_performer.h
+++ b/payload_consumer/delta_performer.h
@@ -158,7 +158,7 @@
// complete metadata. Returns kMetadataParseError if the metadata can't be
// parsed given the payload.
MetadataParseResult ParsePayloadMetadata(const brillo::Blob& payload,
- ErrorCode* error);
+ ErrorCode* error = nullptr);
void set_public_key_path(const std::string& public_key_path) {
public_key_path_ = public_key_path;
@@ -189,7 +189,8 @@
BootControlInterface::Slot target_slot,
const DeltaArchiveManifest& manifest,
const std::string& update_check_response_hash,
- uint64_t* required_size);
+ uint64_t* required_size,
+ ErrorCode* error = nullptr);
protected:
// Exposed as virtual for testing purposes.
@@ -221,7 +222,7 @@
// Parse and move the update instructions of all partitions into our local
// |partitions_| variable based on the version of the payload. Requires the
// manifest to be parsed and valid.
- bool ParseManifestPartitions(ErrorCode* error);
+ bool ParseManifestPartitions(ErrorCode* error = nullptr);
// Appends up to |*count_p| bytes from |*bytes_p| to |buffer_|, but only to
// the extent that the size of |buffer_| does not exceed |max|. Advances
@@ -233,7 +234,7 @@
// sets |*error| accordingly. Otherwise does nothing. Returns |op_result|.
bool HandleOpResult(bool op_result,
const char* op_type_name,
- ErrorCode* error);
+ ErrorCode* error = nullptr);
// Logs the progress of downloading/applying an update.
void LogProgress(const char* message_prefix);
@@ -263,9 +264,9 @@
bool PerformReplaceOperation(const InstallOperation& operation);
bool PerformZeroOrDiscardOperation(const InstallOperation& operation);
bool PerformSourceCopyOperation(const InstallOperation& operation,
- ErrorCode* error);
+ ErrorCode* error = nullptr);
bool PerformDiffOperation(const InstallOperation& operation,
- ErrorCode* error);
+ ErrorCode* error = nullptr);
// Extracts the payload signature message from the current |buffer_| if the
// offset matches the one specified by the manifest. Returns whether the
@@ -300,7 +301,8 @@
// After install_plan_ is filled with partition names and sizes, initialize
// metadata of partitions and map necessary devices before opening devices.
// Also see comment for the static PreparePartitionsForUpdate().
- bool PreparePartitionsForUpdate(uint64_t* required_size);
+ bool PreparePartitionsForUpdate(uint64_t* required_size,
+ ErrorCode* error = nullptr);
// Check if current manifest contains timestamp errors.
// Return:
diff --git a/payload_consumer/file_descriptor.cc b/payload_consumer/file_descriptor.cc
index da76327..ff27b73 100644
--- a/payload_consumer/file_descriptor.cc
+++ b/payload_consumer/file_descriptor.cc
@@ -77,7 +77,7 @@
uint64_t EintrSafeFileDescriptor::BlockDevSize() {
if (fd_ < 0)
return 0;
- struct stat stbuf;
+ struct stat stbuf {};
if (fstat(fd_, &stbuf) < 0) {
PLOG(ERROR) << "Error stat-ing fd " << fd_;
return 0;
@@ -102,7 +102,7 @@
// On some devices, the BLKDISCARD will actually read back as zeros, instead
// of "undefined" data. The BLKDISCARDZEROES ioctl tells whether that's the
// case, so we issue a BLKDISCARD in those cases to speed up the writes.
- unsigned int arg;
+ unsigned int arg{};
if (request == BLKZEROOUT && ioctl(fd_, BLKDISCARDZEROES, &arg) == 0 && arg)
request = BLKDISCARD;
diff --git a/payload_consumer/verified_source_fd.cc b/payload_consumer/verified_source_fd.cc
index 09ac95b..3f17ad7 100644
--- a/payload_consumer/verified_source_fd.cc
+++ b/payload_consumer/verified_source_fd.cc
@@ -159,7 +159,9 @@
source_fd_ = std::make_shared<EintrSafeFileDescriptor>();
if (source_fd_ == nullptr)
return false;
- TEST_AND_RETURN_FALSE_ERRNO(source_fd_->Open(source_path_.c_str(), O_RDONLY));
+ if (!source_fd_->Open(source_path_.c_str(), O_RDONLY)) {
+ PLOG(ERROR) << "Failed to open " << source_path_;
+ }
return true;
}
diff --git a/payload_consumer/xor_extent_writer.cc b/payload_consumer/xor_extent_writer.cc
index 4534c05..fe7eca7 100644
--- a/payload_consumer/xor_extent_writer.cc
+++ b/payload_consumer/xor_extent_writer.cc
@@ -20,15 +20,95 @@
#include "update_engine/common/utils.h"
#include "update_engine/payload_consumer/xor_extent_writer.h"
+#include "update_engine/payload_generator/extent_ranges.h"
#include "update_engine/payload_generator/extent_utils.h"
+#include "update_engine/update_metadata.pb.h"
namespace chromeos_update_engine {
+bool XORExtentWriter::WriteXorCowOp(const uint8_t* bytes,
+ const size_t size,
+ const Extent& xor_ext,
+ const size_t src_offset) {
+ xor_block_data.resize(BlockSize() * xor_ext.num_blocks());
+ const auto src_block = src_offset / BlockSize();
+ ssize_t bytes_read = 0;
+ TEST_AND_RETURN_FALSE_ERRNO(utils::PReadAll(source_fd_,
+ xor_block_data.data(),
+ xor_block_data.size(),
+ src_offset,
+ &bytes_read));
+ if (bytes_read != static_cast<ssize_t>(xor_block_data.size())) {
+ LOG(ERROR) << "bytes_read: " << bytes_read << ", expected to read "
+ << xor_block_data.size() << " at block " << src_block
+ << " offset " << src_offset % BlockSize();
+ return false;
+ }
+
+ std::transform(xor_block_data.cbegin(),
+ xor_block_data.cbegin() + xor_block_data.size(),
+ bytes,
+ xor_block_data.begin(),
+ std::bit_xor<unsigned char>{});
+ TEST_AND_RETURN_FALSE(cow_writer_->AddXorBlocks(xor_ext.start_block(),
+ xor_block_data.data(),
+ xor_block_data.size(),
+ src_block,
+ src_offset % BlockSize()));
+ return true;
+}
+
+bool XORExtentWriter::WriteXorExtent(const uint8_t* bytes,
+ const size_t size,
+ const Extent& xor_ext,
+ const CowMergeOperation* merge_op) {
+ const auto src_block = merge_op->src_extent().start_block() +
+ xor_ext.start_block() -
+ merge_op->dst_extent().start_block();
+ const auto read_end_offset =
+ (src_block + xor_ext.num_blocks()) * BlockSize() + merge_op->src_offset();
+ const auto is_out_of_bound_read =
+ read_end_offset > partition_size_ && partition_size_ != 0;
+ const auto oob_bytes =
+ is_out_of_bound_read ? read_end_offset - partition_size_ : 0;
+ if (is_out_of_bound_read) {
+ if (oob_bytes >= BlockSize()) {
+ LOG(ERROR) << "XOR op overflowed source partition by more than "
+ << BlockSize() << ", " << xor_ext << ", " << merge_op
+ << ", out of bound bytes: " << oob_bytes
+ << ", partition size: " << partition_size_;
+ return false;
+ }
+ if (oob_bytes > merge_op->src_offset()) {
+ LOG(ERROR) << "XOR op overflowed source offset, out of bound bytes: "
+ << oob_bytes << ", source offset: " << merge_op->src_offset();
+ }
+ Extent non_oob_extent =
+ ExtentForRange(xor_ext.start_block(), xor_ext.num_blocks() - 1);
+ if (non_oob_extent.num_blocks() > 0) {
+ TEST_AND_RETURN_FALSE(
+ WriteXorCowOp(bytes,
+ BlockSize() * non_oob_extent.num_blocks(),
+ non_oob_extent,
+ src_block * BlockSize() + merge_op->src_offset()));
+ }
+ const Extent last_block =
+ ExtentForRange(xor_ext.start_block() + xor_ext.num_blocks() - 1, 1);
+ TEST_AND_RETURN_FALSE(
+ WriteXorCowOp(bytes + (xor_ext.num_blocks() - 1) * BlockSize(),
+ BlockSize(),
+ last_block,
+ (src_block + xor_ext.num_blocks() - 1) * BlockSize()));
+ return true;
+ }
+ TEST_AND_RETURN_FALSE(WriteXorCowOp(
+ bytes, size, xor_ext, src_block * BlockSize() + merge_op->src_offset()));
+ return true;
+}
// Returns true on success.
bool XORExtentWriter::WriteExtent(const void* bytes,
const Extent& extent,
const size_t size) {
- brillo::Blob xor_block_data;
const auto xor_extents = xor_map_.GetIntersectingExtents(extent);
for (const auto& xor_ext : xor_extents) {
const auto merge_op_opt = xor_map_.Get(xor_ext);
@@ -60,52 +140,16 @@
<< xor_ext << " xor_map extent: " << merge_op->dst_extent();
return false;
}
- const auto src_offset = merge_op->src_offset();
- const auto src_block = merge_op->src_extent().start_block() +
- xor_ext.start_block() -
- merge_op->dst_extent().start_block();
const auto i = xor_ext.start_block() - extent.start_block();
const auto dst_block_data =
static_cast<const unsigned char*>(bytes) + i * BlockSize();
- const auto is_out_of_bound_read =
- (src_block + xor_ext.num_blocks()) * BlockSize() + src_offset >
- partition_size_ &&
- partition_size_ != 0;
- if (is_out_of_bound_read) {
- LOG(INFO) << "Getting partial read for last block, converting "
- "XOR operation to a regular replace "
- << xor_ext;
- TEST_AND_RETURN_FALSE(
- cow_writer_->AddRawBlocks(xor_ext.start_block(),
- dst_block_data,
- xor_ext.num_blocks() * BlockSize()));
- continue;
- }
- xor_block_data.resize(BlockSize() * xor_ext.num_blocks());
- ssize_t bytes_read = 0;
- TEST_AND_RETURN_FALSE_ERRNO(
- utils::PReadAll(source_fd_,
- xor_block_data.data(),
- xor_block_data.size(),
- src_offset + src_block * BlockSize(),
- &bytes_read));
- if (bytes_read != static_cast<ssize_t>(xor_block_data.size())) {
- LOG(ERROR) << "bytes_read: " << bytes_read << ", expected to read "
- << xor_block_data.size() << " at block " << src_block
- << " offset " << src_offset;
+ if (!WriteXorExtent(dst_block_data,
+ xor_ext.num_blocks() * BlockSize(),
+ xor_ext,
+ merge_op)) {
+ LOG(ERROR) << "Failed to write XOR extent " << xor_ext;
return false;
}
-
- std::transform(xor_block_data.cbegin(),
- xor_block_data.cbegin() + xor_block_data.size(),
- dst_block_data,
- xor_block_data.begin(),
- std::bit_xor<unsigned char>{});
- TEST_AND_RETURN_FALSE(cow_writer_->AddXorBlocks(xor_ext.start_block(),
- xor_block_data.data(),
- xor_block_data.size(),
- src_block,
- src_offset));
}
const auto replace_extents = xor_map_.GetNonIntersectingExtents(extent);
return WriteReplaceExtents(replace_extents, extent, bytes, size);
diff --git a/payload_consumer/xor_extent_writer.h b/payload_consumer/xor_extent_writer.h
index 57c99c2..2074ee2 100644
--- a/payload_consumer/xor_extent_writer.h
+++ b/payload_consumer/xor_extent_writer.h
@@ -56,10 +56,19 @@
const Extent& extent,
const void* bytes,
size_t size);
+ bool WriteXorExtent(const uint8_t* bytes,
+ const size_t size,
+ const Extent& xor_ext,
+ const CowMergeOperation* merge_op);
+ bool WriteXorCowOp(const uint8_t* bytes,
+ const size_t size,
+ const Extent& xor_ext,
+ size_t src_offset);
const google::protobuf::RepeatedPtrField<Extent>& src_extents_;
const FileDescriptorPtr source_fd_;
const ExtentMap<const CowMergeOperation*>& xor_map_;
android::snapshot::ICowWriter* cow_writer_;
+ std::vector<uint8_t> xor_block_data;
const size_t partition_size_;
};
diff --git a/payload_consumer/xor_extent_writer_unittest.cc b/payload_consumer/xor_extent_writer_unittest.cc
index 45796a6..827030a 100644
--- a/payload_consumer/xor_extent_writer_unittest.cc
+++ b/payload_consumer/xor_extent_writer_unittest.cc
@@ -14,6 +14,7 @@
// limitations under the License.
//
+#include <algorithm>
#include <memory>
#include <unistd.h>
@@ -22,7 +23,7 @@
#include <gtest/gtest.h>
#include <libsnapshot/mock_cow_writer.h>
-#include "common/utils.h"
+#include "update_engine/common/utils.h"
#include "update_engine/payload_consumer/extent_map.h"
#include "update_engine/payload_consumer/file_descriptor.h"
#include "update_engine/payload_consumer/xor_extent_writer.h"
@@ -44,11 +45,13 @@
ASSERT_EQ(ftruncate64(source_part_.fd, kBlockSize * NUM_BLOCKS), 0);
ASSERT_EQ(ftruncate64(target_part_.fd, kBlockSize * NUM_BLOCKS), 0);
- // Fill source part with 1s, as we are computing XOR between source and
- // target data later.
+ // Fill source part with arbitrary data, as we are computing XOR between
+ // source and target data later.
ASSERT_EQ(lseek(source_part_.fd, 0, SEEK_SET), 0);
brillo::Blob buffer(kBlockSize);
- std::fill(buffer.begin(), buffer.end(), 1);
+ for (size_t i = 0; i < kBlockSize; i++) {
+ buffer[i] = i & 0xFF;
+ }
for (size_t i = 0; i < NUM_BLOCKS; i++) {
ASSERT_EQ(write(source_part_.fd, buffer.data(), buffer.size()),
static_cast<ssize_t>(buffer.size()));
@@ -195,12 +198,13 @@
// [12-14] => [320-322], [20-22] => [420-422], [NUM_BLOCKS-3] => [2-5]
// merge op:
- // [NUM_BLOCKS-1] => [2-3]
+ // [NUM_BLOCKS-1] => [2]
// Expected result:
- // [12-16] should be REPLACE blocks
+ // [320-322] should be REPLACE blocks
// [420-422] should be REPLACE blocks
- // [2-4] should be REPLACE blocks
+ // [2] should be XOR blocks, with 0 offset to avoid out of bound read
+ // [3-5] should be REPLACE BLOCKS
auto zeros = utils::GetReadonlyZeroBlock(kBlockSize * 9);
EXPECT_CALL(cow_writer_, AddRawBlocks(320, zeros->data(), kBlockSize * 3))
@@ -209,15 +213,84 @@
AddRawBlocks(420, zeros->data() + 3 * kBlockSize, kBlockSize * 3))
.WillOnce(Return(true));
- EXPECT_CALL(cow_writer_,
- AddRawBlocks(2, zeros->data() + 6 * kBlockSize, kBlockSize))
+ EXPECT_CALL(cow_writer_, AddXorBlocks(2, _, kBlockSize, NUM_BLOCKS - 1, 0))
.WillOnce(Return(true));
EXPECT_CALL(cow_writer_,
- AddRawBlocks(3, zeros->data() + 7 * kBlockSize, kBlockSize * 2))
+ AddRawBlocks(3, zeros->data() + kBlockSize * 7, kBlockSize * 2))
.WillOnce(Return(true));
ASSERT_TRUE(writer_.Init(op_.dst_extents(), kBlockSize));
ASSERT_TRUE(writer_.Write(zeros->data(), zeros->size()));
}
+TEST_F(XorExtentWriterTest, LastMultiBlockTest) {
+ constexpr auto COW_XOR = CowMergeOperation::COW_XOR;
+
+ const auto op3 = CreateCowMergeOperation(
+ ExtentForRange(NUM_BLOCKS - 4, 4), ExtentForRange(2, 4), COW_XOR, 777);
+ ASSERT_TRUE(xor_map_.AddExtent(op3.dst_extent(), &op3));
+
+ *op_.add_src_extents() = ExtentForRange(NUM_BLOCKS - 4, 4);
+ *op_.add_dst_extents() = ExtentForRange(2, 4);
+ XORExtentWriter writer_{
+ op_, source_fd_, &cow_writer_, xor_map_, NUM_BLOCKS * kBlockSize};
+
+ // OTA op:
+ // [NUM_BLOCKS-4] => [2-5]
+
+ // merge op:
+ // [NUM_BLOCKS-4] => [2-5]
+
+ // Expected result:
+ // [12-16] should be REPLACE blocks
+ // [420-422] should be REPLACE blocks
+ // [2-3] should be XOR blocks
+ // [4] should be XOR blocks with 0 offset to avoid out of bound read
+
+ // Send arbitrary data, just to confirm that XORExtentWriter did XOR the
+ // source data with target data
+ std::vector<uint8_t> op_data(kBlockSize * 4);
+ for (size_t i = 0; i < op_data.size(); i++) {
+ if (i % kBlockSize == 0) {
+ op_data[i] = 1;
+ } else {
+ op_data[i] = (op_data[i - 1] * 3) & 0xFF;
+ }
+ }
+ auto&& verify_xor_data = [source_fd_(source_fd_), &op_data](
+ uint32_t new_block_start,
+ const void* data,
+ size_t size,
+ uint32_t old_block,
+ uint16_t offset) -> bool {
+ std::vector<uint8_t> source_data(size);
+ ssize_t bytes_read{};
+ TEST_AND_RETURN_FALSE_ERRNO(utils::PReadAll(source_fd_,
+ source_data.data(),
+ source_data.size(),
+ old_block * kBlockSize + offset,
+ &bytes_read));
+ TEST_EQ(bytes_read, static_cast<ssize_t>(source_data.size()));
+ std::transform(source_data.begin(),
+ source_data.end(),
+ static_cast<const uint8_t*>(data),
+ source_data.begin(),
+ std::bit_xor<uint8_t>{});
+ if (memcmp(source_data.data(), op_data.data(), source_data.size()) != 0) {
+ LOG(ERROR) << "XOR data received does not appear to be an XOR between "
+ "source and target data";
+ return false;
+ }
+ return true;
+ };
+ EXPECT_CALL(cow_writer_,
+ AddXorBlocks(2, _, kBlockSize * 3, NUM_BLOCKS - 4, 777))
+ .WillOnce(verify_xor_data);
+ EXPECT_CALL(cow_writer_, AddXorBlocks(5, _, kBlockSize, NUM_BLOCKS - 1, 0))
+ .WillOnce(verify_xor_data);
+
+ ASSERT_TRUE(writer_.Init(op_.dst_extents(), kBlockSize));
+ ASSERT_TRUE(writer_.Write(op_data.data(), op_data.size()));
+}
+
} // namespace chromeos_update_engine
diff --git a/payload_generator/delta_diff_generator.cc b/payload_generator/delta_diff_generator.cc
index 46dfb0f..cfec1b9 100644
--- a/payload_generator/delta_diff_generator.cc
+++ b/payload_generator/delta_diff_generator.cc
@@ -110,7 +110,7 @@
return;
}
if (!old_part_.path.empty()) {
- auto generator = MergeSequenceGenerator::Create(*aops_);
+ auto generator = MergeSequenceGenerator::Create(*aops_, new_part_.name);
if (!generator || !generator->Generate(cow_merge_sequence_)) {
LOG(FATAL) << "Failed to generate merge sequence";
}
diff --git a/payload_generator/extent_utils_unittest.cc b/payload_generator/extent_utils_unittest.cc
index 5467aa5..2046bf5 100644
--- a/payload_generator/extent_utils_unittest.cc
+++ b/payload_generator/extent_utils_unittest.cc
@@ -21,7 +21,6 @@
#include <gtest/gtest.h>
-#include "update_engine/common/test_utils.h"
#include "update_engine/payload_consumer/payload_constants.h"
#include "update_engine/payload_generator/extent_ranges.h"
diff --git a/payload_generator/generate_delta_main.cc b/payload_generator/generate_delta_main.cc
index 6daa36c..8fa86f0 100644
--- a/payload_generator/generate_delta_main.cc
+++ b/payload_generator/generate_delta_main.cc
@@ -445,6 +445,11 @@
false,
"Whether to enable LZ4diff feature when processing EROFS images.");
+DEFINE_bool(enable_puffdiff,
+ true,
+ "Whether to enable puffdiff feature. Enabling puffdiff will take "
+ "longer but generated OTA will be smaller.");
+
DEFINE_bool(
enable_zucchini,
true,
@@ -612,6 +617,7 @@
payload_config.enable_vabc_xor = FLAGS_enable_vabc_xor;
payload_config.enable_lz4diff = FLAGS_enable_lz4diff;
payload_config.enable_zucchini = FLAGS_enable_zucchini;
+ payload_config.enable_puffdiff = FLAGS_enable_puffdiff;
payload_config.ParseCompressorTypes(FLAGS_compressor_types);
diff --git a/payload_generator/merge_sequence_generator.cc b/payload_generator/merge_sequence_generator.cc
index cb43221..fb43bfd 100644
--- a/payload_generator/merge_sequence_generator.cc
+++ b/payload_generator/merge_sequence_generator.cc
@@ -19,7 +19,6 @@
#include <algorithm>
#include <limits>
-#include "update_engine/payload_generator/delta_diff_generator.h"
#include "update_engine/payload_generator/extent_ranges.h"
#include "update_engine/payload_generator/extent_utils.h"
#include "update_engine/update_metadata.pb.h"
@@ -177,7 +176,8 @@
}
std::unique_ptr<MergeSequenceGenerator> MergeSequenceGenerator::Create(
- const std::vector<AnnotatedOperation>& aops) {
+ const std::vector<AnnotatedOperation>& aops,
+ std::string_view partition_name) {
std::vector<CowMergeOperation> sequence;
for (const auto& aop : aops) {
@@ -192,25 +192,123 @@
}
}
- std::sort(sequence.begin(), sequence.end());
return std::unique_ptr<MergeSequenceGenerator>(
- new MergeSequenceGenerator(sequence));
+ new MergeSequenceGenerator(sequence, partition_name));
}
-bool MergeSequenceGenerator::FindDependency(
- std::map<CowMergeOperation, std::set<CowMergeOperation>>* result) const {
- CHECK(result);
+template <typename T>
+CowMergeOperation MaxOutDegree(
+ const T& nodes,
+ const std::map<CowMergeOperation, std::set<CowMergeOperation>>&
+ merge_after) {
+ // Rationale for this algorithm:
+ // We only need to remove nodes from the graph if the graph contains a cycle.
+ // Any graph of N nodes has cycle iff number of edges >= N.
+ // So, to restore the graph back to an acyclic state, we need to keep removing
+ // edges until we have <N edges left. To minimize the number of nodes removed,
+ // we always remove the node with maximum out degree.
+ CowMergeOperation best;
+ size_t max_out_degree = 0;
+ const auto has_xor =
+ std::any_of(nodes.begin(),
+ nodes.end(),
+ [&merge_after](const CowMergeOperation& node) {
+ if (node.type() != CowMergeOperation::COW_XOR) {
+ return false;
+ }
+ auto it = merge_after.find(node);
+ if (it == merge_after.end()) {
+ return false;
+ }
+ return it->second.size() > 0;
+ });
+ for (const auto& op : nodes) {
+ if (has_xor && op.type() != CowMergeOperation::COW_XOR) {
+ continue;
+ }
+ const auto out_degree = merge_after.at(op).size();
+ if (out_degree > max_out_degree) {
+ best = op;
+ max_out_degree = out_degree;
+ } else if (out_degree == max_out_degree) {
+ if (op.src_extent().num_blocks() < best.src_extent().num_blocks()) {
+ best = op;
+ }
+ }
+ }
+ CHECK_NE(max_out_degree, 0UL);
+ return best;
+}
+
+template <typename T>
+struct MapKeyIterator {
+ MapKeyIterator<T>& operator++() {
+ ++it;
+ return *this;
+ }
+ MapKeyIterator<T>& operator--() {
+ --it;
+ return *this;
+ }
+ bool operator==(const MapKeyIterator<T>& rhs) const { return it == rhs.it; }
+ bool operator!=(const MapKeyIterator<T>& rhs) const { return it != rhs.it; }
+ auto&& operator->() const { return it->first; }
+ auto&& operator*() const { return it->first; }
+ T it;
+};
+
+template <typename T, typename U>
+struct MapKeyRange {
+ auto begin() const {
+ return MapKeyIterator<typename std::map<T, U>::const_iterator>{map.begin()};
+ }
+ auto end() const {
+ return MapKeyIterator<typename std::map<T, U>::const_iterator>{map.end()};
+ }
+ std::map<T, U> map;
+};
+
+CowMergeOperation MaxOutDegree(
+ const std::map<CowMergeOperation, int>& incoming_edges,
+ const std::map<CowMergeOperation, std::set<CowMergeOperation>>&
+ merge_after) {
+ return MaxOutDegree(MapKeyRange<CowMergeOperation, int>{incoming_edges},
+ merge_after);
+}
+
+// Given a potentially cyclic graph, return a node to remove to break cycles
+// |incoming_edges| stores nodes' in degree, and |merge_after| is an outgoing
+// edge list. For example, |merge_after[a]| returns all nodes which `a` has an
+// out going edge to.
+// The only requirement of this function is to return a node which is in
+// |incoming_edges| . As long as this is satisfied, merge sequence generation
+// will work. Caller will keep removing nodes returned by this function until
+// the graph has no cycles. However, the choice of which node to remove can
+// greatly impact COW sizes. Nodes removed from the graph will be converted to a
+// COW_REPLACE operation, taking more disk space. So this function should try to
+// pick a node which minimizes number of nodes we have to remove. (Modulo the
+// weight of each node, which is how many blocks a CowMergeOperation touches)
+CowMergeOperation PickConvertToRaw(
+ const std::map<CowMergeOperation, int>& incoming_edges,
+ const std::map<CowMergeOperation, std::set<CowMergeOperation>>&
+ merge_after) {
+ return MaxOutDegree(incoming_edges, merge_after);
+}
+
+std::map<CowMergeOperation, std::set<CowMergeOperation>>
+MergeSequenceGenerator::FindDependency(
+ const std::vector<CowMergeOperation>& operations) {
LOG(INFO) << "Finding dependencies";
// Since the OTA operation may reuse some source blocks, use the binary
// search on sorted dst extents to find overlaps.
std::map<CowMergeOperation, std::set<CowMergeOperation>> merge_after;
- for (const auto& op : operations_) {
+ for (const auto& op : operations) {
// lower bound (inclusive): dst extent's end block >= src extent's start
// block.
const auto lower_it = std::lower_bound(
- operations_.begin(),
- operations_.end(),
+ operations.begin(),
+ operations.end(),
op,
[](const CowMergeOperation& it, const CowMergeOperation& op) {
auto dst_end_block =
@@ -220,7 +318,7 @@
// upper bound: dst extent's start block > src extent's end block
const auto upper_it = std::upper_bound(
lower_it,
- operations_.end(),
+ operations.end(),
op,
[](const CowMergeOperation& op, const CowMergeOperation& it) {
auto src_end_block =
@@ -244,18 +342,12 @@
}
}
- *result = std::move(merge_after);
- return true;
+ return merge_after;
}
bool MergeSequenceGenerator::Generate(
std::vector<CowMergeOperation>* sequence) const {
sequence->clear();
- std::map<CowMergeOperation, std::set<CowMergeOperation>> merge_after;
- if (!FindDependency(&merge_after)) {
- LOG(ERROR) << "Failed to find dependencies";
- return false;
- }
LOG(INFO) << "Generating sequence";
@@ -263,7 +355,7 @@
// operations to discard to break cycles; thus yielding a deterministic
// sequence.
std::map<CowMergeOperation, int> incoming_edges;
- for (const auto& it : merge_after) {
+ for (const auto& it : merge_after_) {
for (const auto& blocked : it.second) {
// Value is default initialized to 0.
incoming_edges[blocked] += 1;
@@ -289,7 +381,11 @@
merge_sequence.insert(
merge_sequence.end(), free_operations.begin(), free_operations.end());
} else {
- auto to_convert = incoming_edges.begin()->first;
+ auto to_convert = PickConvertToRaw(incoming_edges, merge_after_);
+ // The operation we pick must be one of the nodes not already in merge
+ // sequence.
+ CHECK(incoming_edges.find(to_convert) != incoming_edges.end());
+
free_operations.insert(to_convert);
convert_to_raw.insert(to_convert);
LOG(INFO) << "Converting operation to raw " << to_convert;
@@ -302,7 +398,7 @@
// Now that this particular operation is merged, other operations
// blocked by this one may be free. Decrement the count of blocking
// operations, and set up the free operations for the next iteration.
- for (const auto& blocked : merge_after[op]) {
+ for (const auto& blocked : merge_after_.at(op)) {
auto it = incoming_edges.find(blocked);
if (it == incoming_edges.end()) {
continue;
@@ -347,7 +443,8 @@
}
LOG(INFO) << "Blocks in merge sequence " << blocks_in_sequence
- << ", blocks in raw " << blocks_in_raw;
+ << ", blocks in raw " << blocks_in_raw << ", partition "
+ << partition_name_;
if (!ValidateSequence(merge_sequence)) {
LOG(ERROR) << "Invalid Sequence";
return false;
diff --git a/payload_generator/merge_sequence_generator.h b/payload_generator/merge_sequence_generator.h
index d3b5eb9..083eedf 100644
--- a/payload_generator/merge_sequence_generator.h
+++ b/payload_generator/merge_sequence_generator.h
@@ -24,8 +24,6 @@
#include <vector>
#include "update_engine/payload_generator/annotated_operation.h"
-#include "update_engine/payload_generator/extent_ranges.h"
-#include "update_engine/payload_generator/extent_utils.h"
#include "update_engine/update_metadata.pb.h"
namespace chromeos_update_engine {
@@ -46,12 +44,25 @@
// read after write will happen by following the sequence. When there is a
// cycle, we will omit some operations in the list. Therefore, the result
// sequence may not contain all blocks in the input list.
+
+template <typename T>
+T&& Sort(T&& container) {
+ std::sort(container.begin(), container.end());
+ return container;
+}
+
class MergeSequenceGenerator {
public:
- // Creates an object from a list of OTA InstallOperations. Returns nullptr on
- // failure.
+ // Creates an object from a list of OTA InstallOperations. Returns nullptr
+ // on failure.
static std::unique_ptr<MergeSequenceGenerator> Create(
- const std::vector<AnnotatedOperation>& aops);
+ const std::vector<AnnotatedOperation>& aops,
+ std::string_view partition_name = "");
+ explicit MergeSequenceGenerator(std::vector<CowMergeOperation> transfers,
+ std::string_view partition_name)
+ : operations_(std::move(Sort(transfers))),
+ merge_after_(FindDependency(operations_)),
+ partition_name_(partition_name) {}
// Checks that no read after write happens in the given sequence.
static bool ValidateSequence(const std::vector<CowMergeOperation>& sequence);
@@ -59,17 +70,25 @@
// |sequence|. Returns false on failure.
bool Generate(std::vector<CowMergeOperation>* sequence) const;
+ const std::vector<CowMergeOperation>& GetOperations() const {
+ return operations_;
+ }
+ const std::map<CowMergeOperation, std::set<CowMergeOperation>>&
+ GetDependencyMap() const {
+ return merge_after_;
+ }
+
private:
friend class MergeSequenceGeneratorTest;
- explicit MergeSequenceGenerator(std::vector<CowMergeOperation> transfers)
- : operations_(std::move(transfers)) {}
// For a given merge operation, finds all the operations that should merge
- // after myself. Put the result in |merge_after|.
- bool FindDependency(std::map<CowMergeOperation, std::set<CowMergeOperation>>*
- merge_after) const;
+ // after myself. Put the result in |merge_after|. |operations| must be sorted
+ static std::map<CowMergeOperation, std::set<CowMergeOperation>>
+ FindDependency(const std::vector<CowMergeOperation>& operations);
// The list of CowMergeOperations to sort.
const std::vector<CowMergeOperation> operations_;
+ const std::map<CowMergeOperation, std::set<CowMergeOperation>> merge_after_;
+ const std::string_view partition_name_;
};
void SplitSelfOverlapping(const Extent& src_extent,
diff --git a/payload_generator/merge_sequence_generator_unittest.cc b/payload_generator/merge_sequence_generator_unittest.cc
index 86d4fdd..b2c86a2 100644
--- a/payload_generator/merge_sequence_generator_unittest.cc
+++ b/payload_generator/merge_sequence_generator_unittest.cc
@@ -17,16 +17,19 @@
#include <algorithm>
#include <vector>
+#include <android-base/file.h>
#include <gtest/gtest.h>
-#include "update_engine/payload_consumer/payload_constants.h"
-#include "update_engine/payload_generator/delta_diff_generator.h"
+#include "update_engine/common/test_utils.h"
+#include "update_engine/common/utils.h"
#include "update_engine/payload_generator/extent_ranges.h"
#include "update_engine/payload_generator/extent_utils.h"
#include "update_engine/payload_generator/merge_sequence_generator.h"
#include "update_engine/update_metadata.pb.h"
namespace chromeos_update_engine {
+using test_utils::GetBuildArtifactsPath;
+
CowMergeOperation CreateCowMergeOperation(const Extent& src_extent,
const Extent& dst_extent) {
return CreateCowMergeOperation(
@@ -43,17 +46,14 @@
std::vector<CowMergeOperation> transfers,
std::map<CowMergeOperation, std::set<CowMergeOperation>>* result) {
std::sort(transfers.begin(), transfers.end());
- MergeSequenceGenerator generator(std::move(transfers));
- ASSERT_TRUE(generator.FindDependency(result));
+ *result = MergeSequenceGenerator::FindDependency(transfers);
}
- void GenerateSequence(std::vector<CowMergeOperation> transfers,
- const std::vector<CowMergeOperation>& expected) {
+ void GenerateSequence(std::vector<CowMergeOperation> transfers) {
std::sort(transfers.begin(), transfers.end());
- MergeSequenceGenerator generator(std::move(transfers));
+ MergeSequenceGenerator generator(std::move(transfers), "");
std::vector<CowMergeOperation> sequence;
ASSERT_TRUE(generator.Generate(&sequence));
- ASSERT_EQ(expected, sequence);
}
};
@@ -177,24 +177,18 @@
CreateCowMergeOperation(ExtentForRange(25, 10), ExtentForRange(30, 10)),
};
- std::vector<CowMergeOperation> expected{
- transfers[0], transfers[2], transfers[1]};
- GenerateSequence(transfers, expected);
+ GenerateSequence(transfers);
}
TEST_F(MergeSequenceGeneratorTest, GenerateSequenceWithCycles) {
std::vector<CowMergeOperation> transfers = {
- CreateCowMergeOperation(ExtentForRange(25, 10), ExtentForRange(30, 10)),
+ CreateCowMergeOperation(ExtentForRange(15, 10), ExtentForRange(30, 10)),
CreateCowMergeOperation(ExtentForRange(30, 10), ExtentForRange(40, 10)),
- CreateCowMergeOperation(ExtentForRange(40, 10), ExtentForRange(25, 10)),
- CreateCowMergeOperation(ExtentForRange(10, 10), ExtentForRange(15, 10)),
+ CreateCowMergeOperation(ExtentForRange(40, 10), ExtentForRange(15, 10)),
+ CreateCowMergeOperation(ExtentForRange(10, 10), ExtentForRange(5, 10)),
};
- // file 1,2,3 form a cycle. And file3, whose dst ext has smallest offset,
- // will be converted to raw blocks
- std::vector<CowMergeOperation> expected{
- transfers[3], transfers[1], transfers[0]};
- GenerateSequence(transfers, expected);
+ GenerateSequence(transfers);
}
TEST_F(MergeSequenceGeneratorTest, GenerateSequenceMultipleCycles) {
@@ -204,15 +198,12 @@
CreateCowMergeOperation(ExtentForRange(24, 5), ExtentForRange(35, 5)),
CreateCowMergeOperation(ExtentForRange(30, 10), ExtentForRange(15, 10)),
// cycle 2
- CreateCowMergeOperation(ExtentForRange(55, 10), ExtentForRange(60, 10)),
+ CreateCowMergeOperation(ExtentForRange(50, 10), ExtentForRange(60, 10)),
CreateCowMergeOperation(ExtentForRange(60, 10), ExtentForRange(70, 10)),
- CreateCowMergeOperation(ExtentForRange(70, 10), ExtentForRange(55, 10)),
+ CreateCowMergeOperation(ExtentForRange(70, 10), ExtentForRange(50, 10)),
};
- // file 3, 6 will be converted to raw.
- std::vector<CowMergeOperation> expected{
- transfers[1], transfers[0], transfers[4], transfers[3]};
- GenerateSequence(transfers, expected);
+ GenerateSequence(transfers);
}
void ValidateSplitSequence(const Extent& src_extent, const Extent& dst_extent) {
@@ -265,17 +256,14 @@
ExtentForRange(15, 10),
CowMergeOperation::COW_XOR),
// cycle 2
- CreateCowMergeOperation(ExtentForRange(55, 10), ExtentForRange(60, 10)),
+ CreateCowMergeOperation(ExtentForRange(50, 10), ExtentForRange(60, 10)),
CreateCowMergeOperation(ExtentForRange(60, 10),
ExtentForRange(70, 10),
CowMergeOperation::COW_XOR),
- CreateCowMergeOperation(ExtentForRange(70, 10), ExtentForRange(55, 10)),
+ CreateCowMergeOperation(ExtentForRange(70, 10), ExtentForRange(50, 10)),
};
- // file 3, 6 will be converted to raw.
- std::vector<CowMergeOperation> expected{
- transfers[1], transfers[0], transfers[4], transfers[3]};
- GenerateSequence(transfers, expected);
+ GenerateSequence(transfers);
}
TEST_F(MergeSequenceGeneratorTest, CreateGeneratorWithXor) {
@@ -424,4 +412,24 @@
ASSERT_TRUE(generator->ValidateSequence(sequence));
}
+TEST_F(MergeSequenceGeneratorTest, ActualPayloadTest) {
+ auto payload_path =
+ GetBuildArtifactsPath("testdata/cycle_nodes_product_no_xor.bin");
+ ASSERT_FALSE(payload_path.empty());
+ ASSERT_TRUE(utils::FileExists(payload_path.c_str()));
+ PartitionUpdate part;
+ std::string payload;
+ android::base::ReadFileToString(payload_path, &payload);
+ part.ParseFromString(payload);
+ part.set_partition_name("product");
+ std::vector<CowMergeOperation> ops;
+ ops.reserve(part.merge_operations_size());
+ for (const auto& op : part.merge_operations()) {
+ ops.emplace_back(op);
+ }
+ MergeSequenceGenerator generator(ops, part.partition_name());
+ std::vector<CowMergeOperation> sequence;
+ ASSERT_TRUE(generator.Generate(&sequence));
+}
+
} // namespace chromeos_update_engine
diff --git a/payload_generator/payload_generation_config.cc b/payload_generator/payload_generation_config.cc
index f8469ea..a974180 100644
--- a/payload_generator/payload_generation_config.cc
+++ b/payload_generator/payload_generation_config.cc
@@ -38,6 +38,7 @@
#include "update_engine/payload_generator/mapfile_filesystem.h"
#include "update_engine/payload_generator/raw_filesystem.h"
#include "update_engine/payload_generator/squashfs_filesystem.h"
+#include "update_engine/update_metadata.pb.h"
using std::string;
@@ -393,6 +394,8 @@
case InstallOperation::LZ4DIFF_BSDIFF:
case InstallOperation::LZ4DIFF_PUFFDIFF:
return enable_lz4diff;
+ case InstallOperation::PUFFDIFF:
+ return enable_puffdiff;
default:
return true;
}
diff --git a/payload_generator/payload_generation_config.h b/payload_generator/payload_generation_config.h
index 225237a..0256a9d 100644
--- a/payload_generator/payload_generation_config.h
+++ b/payload_generator/payload_generation_config.h
@@ -261,6 +261,9 @@
// Whether to enable zucchini ops
bool enable_zucchini = true;
+ // Whether to enable puffdiff ops
+ bool enable_puffdiff = true;
+
std::string security_patch_level;
uint32_t max_threads = 0;
diff --git a/testdata/cycle_nodes_product.bin b/testdata/cycle_nodes_product.bin
new file mode 100644
index 0000000..0042934
--- /dev/null
+++ b/testdata/cycle_nodes_product.bin
Binary files differ
diff --git a/testdata/cycle_nodes_product_no_xor.bin b/testdata/cycle_nodes_product_no_xor.bin
new file mode 100644
index 0000000..44ed4fc
--- /dev/null
+++ b/testdata/cycle_nodes_product_no_xor.bin
Binary files differ
diff --git a/testdata/cycle_nodes_system.bin b/testdata/cycle_nodes_system.bin
new file mode 100644
index 0000000..d1fc0be
--- /dev/null
+++ b/testdata/cycle_nodes_system.bin
Binary files differ
diff --git a/testdata/cycle_nodes_system_dlkm.bin b/testdata/cycle_nodes_system_dlkm.bin
new file mode 100644
index 0000000..1e6bdb7
--- /dev/null
+++ b/testdata/cycle_nodes_system_dlkm.bin
Binary files differ
diff --git a/testdata/cycle_nodes_system_ext.bin b/testdata/cycle_nodes_system_ext.bin
new file mode 100644
index 0000000..80ab049
--- /dev/null
+++ b/testdata/cycle_nodes_system_ext.bin
Binary files differ
diff --git a/testdata/cycle_nodes_vendor.bin b/testdata/cycle_nodes_vendor.bin
new file mode 100644
index 0000000..a5f72d9
--- /dev/null
+++ b/testdata/cycle_nodes_vendor.bin
Binary files differ
diff --git a/testdata/cycle_nodes_vendor_dlkm.bin b/testdata/cycle_nodes_vendor_dlkm.bin
new file mode 100644
index 0000000..88e6afc
--- /dev/null
+++ b/testdata/cycle_nodes_vendor_dlkm.bin
Binary files differ
diff --git a/update_engine.conf b/update_engine.conf
index 2d3a655..22b59d9 100644
--- a/update_engine.conf
+++ b/update_engine.conf
@@ -1,2 +1,2 @@
PAYLOAD_MAJOR_VERSION=2
-PAYLOAD_MINOR_VERSION=8
+PAYLOAD_MINOR_VERSION=9