Allow update_engine to communicate with apexd for size calculation am: 24a8279469
Original change: https://android-review.googlesource.com/c/platform/system/update_engine/+/1587413
MUST ONLY BE SUBMITTED BY AUTOMERGER
Change-Id: I22a75af1926ffd36d429e0b5afdb8f559f505352
diff --git a/Android.bp b/Android.bp
index 4ec17f2..1658533 100644
--- a/Android.bp
+++ b/Android.bp
@@ -332,6 +332,7 @@
"PlatformProperties",
],
shared_libs: [
+ "apex_aidl_interface-cpp",
"libandroid_net",
"libbase",
"libbinder",
@@ -367,6 +368,7 @@
srcs: [
":libupdate_engine_aidl",
"common/system_state.cc",
+ "aosp/apex_handler_android.cc",
"aosp/binder_service_android.cc",
"aosp/binder_service_stable_android.cc",
"aosp/daemon_android.cc",
@@ -740,6 +742,7 @@
test_suites: ["device-tests"],
srcs: [
+ "aosp/apex_handler_android_unittest.cc",
"aosp/dynamic_partition_control_android_unittest.cc",
"aosp/update_attempter_android_unittest.cc",
"certificate_checker_unittest.cc",
diff --git a/aosp/apex_handler_android.cc b/aosp/apex_handler_android.cc
new file mode 100644
index 0000000..cdbc983
--- /dev/null
+++ b/aosp/apex_handler_android.cc
@@ -0,0 +1,98 @@
+//
+// Copyright (C) 2021 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 <utility>
+
+#include <base/files/file_util.h>
+
+#include "update_engine/aosp/apex_handler_android.h"
+#include "update_engine/common/utils.h"
+
+namespace chromeos_update_engine {
+
+// Don't change this path... apexd relies on it.
+constexpr const char* kApexReserveSpaceDir = "/data/apex/ota_reserved";
+
+uint64_t ApexHandlerAndroid::CalculateSize(
+ const std::vector<ApexInfo>& apex_infos) const {
+ return CalculateSize(apex_infos, GetApexService());
+}
+
+uint64_t ApexHandlerAndroid::CalculateSize(
+ const std::vector<ApexInfo>& apex_infos,
+ android::sp<android::apex::IApexService> apex_service) const {
+ // The safest option is to allocate space for every compressed APEX
+ uint64_t size_required_default = 0;
+
+ // We might not need to decompress every APEX. Communicate with apexd to get
+ // accurate requirement.
+ int64_t size_from_apexd;
+ android::apex::CompressedApexInfoList compressed_apex_info_list;
+
+ for (const auto& apex_info : apex_infos) {
+ if (!apex_info.is_compressed()) {
+ continue;
+ }
+
+ size_required_default += apex_info.decompressed_size();
+
+ android::apex::CompressedApexInfo compressed_apex_info;
+ compressed_apex_info.moduleName = apex_info.package_name();
+ compressed_apex_info.versionCode = apex_info.version();
+ compressed_apex_info.decompressedSize = apex_info.decompressed_size();
+ compressed_apex_info_list.apexInfos.emplace_back(
+ std::move(compressed_apex_info));
+ }
+ if (size_required_default == 0 || apex_service == nullptr) {
+ return size_required_default;
+ }
+
+ auto result = apex_service->calculateSizeForCompressedApex(
+ compressed_apex_info_list, &size_from_apexd);
+ if (!result.isOk()) {
+ return size_required_default;
+ }
+ return size_from_apexd;
+}
+
+bool ApexHandlerAndroid::AllocateSpace(const uint64_t size_required) const {
+ return AllocateSpace(size_required, kApexReserveSpaceDir);
+}
+
+bool ApexHandlerAndroid::AllocateSpace(const uint64_t size_required,
+ const std::string& dir_path) const {
+ if (size_required == 0) {
+ return true;
+ }
+ base::FilePath path{dir_path};
+ // The filename is not important, it just needs to be under
+ // kApexReserveSpaceDir. We call it "full.tmp" because the current space
+ // estimation is simply adding up all decompressed sizes.
+ path = path.Append("full.tmp");
+ return utils::ReserveStorageSpace(path.value().c_str(), size_required);
+}
+
+android::sp<android::apex::IApexService> ApexHandlerAndroid::GetApexService()
+ const {
+ auto binder = android::defaultServiceManager()->waitForService(
+ android::String16("apexservice"));
+ if (binder == nullptr) {
+ return nullptr;
+ }
+ return android::interface_cast<android::apex::IApexService>(binder);
+}
+
+} // namespace chromeos_update_engine
diff --git a/aosp/apex_handler_android.h b/aosp/apex_handler_android.h
new file mode 100644
index 0000000..aac1cd9
--- /dev/null
+++ b/aosp/apex_handler_android.h
@@ -0,0 +1,48 @@
+//
+// Copyright (C) 2021 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_UPDATE_ENGINE_AOSP_APEX_HANDLER_ANDROID_H_
+#define SYSTEM_UPDATE_ENGINE_AOSP_APEX_HANDLER_ANDROID_H_
+
+#include <string>
+#include <vector>
+
+#include <android/apex/IApexService.h>
+#include <binder/IServiceManager.h>
+
+#include "update_engine/aosp/apex_handler_interface.h"
+#include "update_engine/update_metadata.pb.h"
+
+namespace chromeos_update_engine {
+
+class ApexHandlerAndroid : virtual public ApexHandlerInterface {
+ public:
+ uint64_t CalculateSize(const std::vector<ApexInfo>& apex_infos) const;
+ bool AllocateSpace(const uint64_t size_required) const;
+
+ private:
+ friend class ApexHandlerAndroidTest;
+ android::sp<android::apex::IApexService> GetApexService() const;
+ uint64_t CalculateSize(
+ const std::vector<ApexInfo>& apex_infos,
+ android::sp<android::apex::IApexService> apex_service) const;
+ bool AllocateSpace(const uint64_t size_required,
+ const std::string& dir_path) const;
+};
+
+} // namespace chromeos_update_engine
+
+#endif // SYSTEM_UPDATE_ENGINE_AOSP_APEX_HANDLER_ANDROID_H_
diff --git a/aosp/apex_handler_android_unittest.cc b/aosp/apex_handler_android_unittest.cc
new file mode 100644
index 0000000..3a99f79
--- /dev/null
+++ b/aosp/apex_handler_android_unittest.cc
@@ -0,0 +1,109 @@
+//
+// Copyright (C) 2021 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 <utility>
+#include <filesystem>
+
+#include "update_engine/aosp/apex_handler_android.h"
+
+#include <android-base/file.h>
+#include <android-base/strings.h>
+#include <gtest/gtest.h>
+
+using android::base::EndsWith;
+
+namespace chromeos_update_engine {
+
+namespace fs = std::filesystem;
+
+class ApexHandlerAndroidTest : public ::testing::Test {
+ protected:
+ ApexHandlerAndroidTest() = default;
+
+ android::sp<android::apex::IApexService> GetApexService() const {
+ return apex_handler_.GetApexService();
+ }
+
+ uint64_t CalculateSize(
+ const std::vector<ApexInfo>& apex_infos,
+ android::sp<android::apex::IApexService> apex_service) const {
+ return apex_handler_.CalculateSize(apex_infos, apex_service);
+ }
+
+ bool AllocateSpace(const uint64_t size_required,
+ const std::string& dir_path) const {
+ return apex_handler_.AllocateSpace(size_required, dir_path);
+ }
+
+ ApexInfo CreateApexInfo(const std::string& package_name,
+ int version,
+ bool is_compressed,
+ int decompressed_size) {
+ ApexInfo result;
+ result.set_package_name(package_name);
+ result.set_version(version);
+ result.set_is_compressed(is_compressed);
+ result.set_decompressed_size(decompressed_size);
+ return std::move(result);
+ }
+
+ ApexHandlerAndroid apex_handler_;
+};
+
+// TODO(b/172911822): Once apexd has more optimized response for CalculateSize,
+// improve this test
+TEST_F(ApexHandlerAndroidTest, CalculateSize) {
+ std::vector<ApexInfo> apex_infos;
+ ApexInfo compressed_apex_1 = CreateApexInfo("sample1", 1, true, 10);
+ ApexInfo compressed_apex_2 = CreateApexInfo("sample2", 2, true, 20);
+ apex_infos.push_back(compressed_apex_1);
+ apex_infos.push_back(compressed_apex_2);
+ auto apex_service = GetApexService();
+ EXPECT_TRUE(apex_service != nullptr) << "Apexservice not found";
+ int required_size = CalculateSize(apex_infos, apex_service);
+ EXPECT_EQ(required_size, 30);
+}
+
+TEST_F(ApexHandlerAndroidTest, AllocateSpace) {
+ // Allocating 0 space should be a no op
+ TemporaryDir td;
+ EXPECT_TRUE(AllocateSpace(0, td.path));
+ EXPECT_TRUE(fs::is_empty(td.path));
+
+ // Allocating non-zero space should create a file with tmp suffix
+ EXPECT_TRUE(AllocateSpace(2 << 20, td.path));
+ EXPECT_FALSE(fs::is_empty(td.path));
+ int num_of_file = 0;
+ for (const auto& entry : fs::directory_iterator(td.path)) {
+ num_of_file++;
+ EXPECT_TRUE(EndsWith(entry.path().string(), ".tmp"));
+ EXPECT_EQ(fs::file_size(entry.path()), 2u << 20);
+ }
+ EXPECT_EQ(num_of_file, 1);
+
+ // AllocateSpace should be safe to call twice
+ EXPECT_TRUE(AllocateSpace(100, td.path));
+ EXPECT_FALSE(fs::is_empty(td.path));
+ num_of_file = 0;
+ for (const auto& entry : fs::directory_iterator(td.path)) {
+ num_of_file++;
+ EXPECT_TRUE(EndsWith(entry.path().string(), ".tmp"));
+ EXPECT_EQ(fs::file_size(entry.path()), 100u);
+ }
+ EXPECT_EQ(num_of_file, 1);
+}
+
+} // namespace chromeos_update_engine
diff --git a/aosp/apex_handler_interface.h b/aosp/apex_handler_interface.h
new file mode 100644
index 0000000..c3399b6
--- /dev/null
+++ b/aosp/apex_handler_interface.h
@@ -0,0 +1,36 @@
+//
+// Copyright (C) 2021 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_UPDATE_ENGINE_AOSP_APEX_HANDLER_INTERFACE_H_
+#define SYSTEM_UPDATE_ENGINE_AOSP_APEX_HANDLER_INTERFACE_H_
+
+#include <vector>
+
+#include "update_engine/update_metadata.pb.h"
+
+namespace chromeos_update_engine {
+
+class ApexHandlerInterface {
+ public:
+ virtual ~ApexHandlerInterface() = default;
+ virtual uint64_t CalculateSize(
+ const std::vector<ApexInfo>& apex_infos) const = 0;
+ virtual bool AllocateSpace(const uint64_t size_required) const = 0;
+};
+
+} // namespace chromeos_update_engine
+
+#endif // SYSTEM_UPDATE_ENGINE_AOSP_APEX_HANDLER_INTERFACE_H_
diff --git a/aosp/daemon_state_android.cc b/aosp/daemon_state_android.cc
index 9bdd175..fc89d73 100644
--- a/aosp/daemon_state_android.cc
+++ b/aosp/daemon_state_android.cc
@@ -18,6 +18,7 @@
#include <base/logging.h>
+#include "update_engine/aosp/apex_handler_android.h"
#include "update_engine/aosp/update_attempter_android.h"
#include "update_engine/common/boot_control.h"
#include "update_engine/common/boot_control_stub.h"
@@ -64,8 +65,12 @@
certificate_checker_->Init();
// Initialize the UpdateAttempter before the UpdateManager.
- update_attempter_.reset(new UpdateAttempterAndroid(
- this, prefs_.get(), boot_control_.get(), hardware_.get()));
+ update_attempter_.reset(
+ new UpdateAttempterAndroid(this,
+ prefs_.get(),
+ boot_control_.get(),
+ hardware_.get(),
+ std::make_unique<ApexHandlerAndroid>()));
return true;
}
diff --git a/aosp/sideload_main.cc b/aosp/sideload_main.cc
index 3cbc0c7..bf015c9 100644
--- a/aosp/sideload_main.cc
+++ b/aosp/sideload_main.cc
@@ -154,8 +154,11 @@
return false;
}
- UpdateAttempterAndroid update_attempter(
- &sideload_daemon_state, &prefs, boot_control.get(), hardware.get());
+ UpdateAttempterAndroid update_attempter(&sideload_daemon_state,
+ &prefs,
+ boot_control.get(),
+ hardware.get(),
+ nullptr);
update_attempter.Init();
TEST_AND_RETURN_FALSE(update_attempter.ApplyPayload(
diff --git a/aosp/update_attempter_android.cc b/aosp/update_attempter_android.cc
index 5523a58..c685855 100644
--- a/aosp/update_attempter_android.cc
+++ b/aosp/update_attempter_android.cc
@@ -71,9 +71,6 @@
namespace chromeos_update_engine {
-// Don't change this path... apexd relies on it.
-constexpr const char* kApexReserveSpaceDir = "/data/apex/ota_reserved";
-
namespace {
// Minimum threshold to broadcast an status update in progress and time.
@@ -136,11 +133,13 @@
DaemonStateInterface* daemon_state,
PrefsInterface* prefs,
BootControlInterface* boot_control,
- HardwareInterface* hardware)
+ HardwareInterface* hardware,
+ std::unique_ptr<ApexHandlerInterface> apex_handler)
: daemon_state_(daemon_state),
prefs_(prefs),
boot_control_(boot_control),
hardware_(hardware),
+ apex_handler_android_(std::move(apex_handler)),
processor_(new ActionProcessor()),
clock_(new Clock()) {
metrics_reporter_ = metrics::CreateMetricsReporter(
@@ -967,28 +966,6 @@
return GetCurrentSlot() == 0 ? 1 : 0;
}
-static uint64_t allocateSpaceForApex(const DeltaArchiveManifest& manifest) {
- // TODO(b/178696931) call apexd's binder once there is one.
- uint64_t size_required = 0;
- for (const auto& apex_info : manifest.apex_info()) {
- if (apex_info.is_compressed()) {
- size_required += apex_info.decompressed_size();
- }
- }
- if (size_required == 0) {
- return 0;
- }
- base::FilePath path{kApexReserveSpaceDir};
- // The filename is not important, it just needs to be under
- // kApexReserveSpaceDir. We call it "full.tmp" because the current space
- // estimation is simply adding up all decompressed sizes.
- path = path.Append("full.tmp");
- if (!utils::ReserveStorageSpace(path.value().c_str(), size_required)) {
- return size_required;
- }
- return 0;
-}
-
uint64_t UpdateAttempterAndroid::AllocateSpaceForPayload(
const std::string& metadata_filename,
const vector<string>& key_value_pair_headers,
@@ -1002,6 +979,13 @@
return 0;
}
+ std::vector<ApexInfo> apex_infos(manifest.apex_info().begin(),
+ manifest.apex_info().end());
+ uint64_t apex_size_required = 0;
+ if (apex_handler_android_ != nullptr) {
+ apex_size_required = apex_handler_android_->CalculateSize(apex_infos);
+ }
+
string payload_id = GetPayloadId(headers);
uint64_t required_size = 0;
if (!DeltaPerformer::PreparePartitionsForUpdate(prefs_,
@@ -1015,14 +999,17 @@
return 0;
} else {
LOG(ERROR) << "Insufficient space for payload: " << required_size
+ << " bytes, apex decompression: " << apex_size_required
<< " bytes";
- return required_size;
+ return required_size + apex_size_required;
}
}
- const auto apex_size = allocateSpaceForApex(manifest);
- if (apex_size != 0) {
- LOG(ERROR) << "Insufficient space for apex decompression: " << apex_size;
- return required_size + apex_size;
+
+ if (apex_size_required > 0 && apex_handler_android_ != nullptr &&
+ !apex_handler_android_->AllocateSpace(apex_size_required)) {
+ LOG(ERROR) << "Insufficient space for apex decompression: "
+ << apex_size_required << " bytes";
+ return apex_size_required;
}
LOG(INFO) << "Successfully allocated space for payload.";
diff --git a/aosp/update_attempter_android.h b/aosp/update_attempter_android.h
index 499f8f6..70938bc 100644
--- a/aosp/update_attempter_android.h
+++ b/aosp/update_attempter_android.h
@@ -26,6 +26,7 @@
#include <android-base/unique_fd.h>
#include <base/time/time.h>
+#include "update_engine/aosp/apex_handler_interface.h"
#include "update_engine/aosp/service_delegate_android_interface.h"
#include "update_engine/client_library/include/update_engine/update_status.h"
#include "update_engine/common/action_processor.h"
@@ -57,7 +58,8 @@
UpdateAttempterAndroid(DaemonStateInterface* daemon_state,
PrefsInterface* prefs,
BootControlInterface* boot_control_,
- HardwareInterface* hardware_);
+ HardwareInterface* hardware_,
+ std::unique_ptr<ApexHandlerInterface> apex_handler);
~UpdateAttempterAndroid() override;
// Further initialization to be done post construction.
@@ -205,6 +207,8 @@
BootControlInterface* boot_control_;
HardwareInterface* hardware_;
+ std::unique_ptr<ApexHandlerInterface> apex_handler_android_;
+
// Last status notification timestamp used for throttling. Use monotonic
// TimeTicks to ensure that notifications are sent even if the system clock is
// set back in the middle of an update.
diff --git a/aosp/update_attempter_android_unittest.cc b/aosp/update_attempter_android_unittest.cc
index 173e943..f799df3 100644
--- a/aosp/update_attempter_android_unittest.cc
+++ b/aosp/update_attempter_android_unittest.cc
@@ -69,7 +69,7 @@
FakeHardware hardware_;
UpdateAttempterAndroid update_attempter_android_{
- &daemon_state_, &prefs_, &boot_control_, &hardware_};
+ &daemon_state_, &prefs_, &boot_control_, &hardware_, nullptr};
FakeClock* clock_;
testing::NiceMock<MockMetricsReporter>* metrics_reporter_;