diff options
-rw-r--r-- | Android.bp | 3 | ||||
-rw-r--r-- | aosp/apex_handler_android.cc | 98 | ||||
-rw-r--r-- | aosp/apex_handler_android.h | 48 | ||||
-rw-r--r-- | aosp/apex_handler_android_unittest.cc | 109 | ||||
-rw-r--r-- | aosp/apex_handler_interface.h | 36 | ||||
-rw-r--r-- | aosp/daemon_state_android.cc | 9 | ||||
-rw-r--r-- | aosp/sideload_main.cc | 7 | ||||
-rw-r--r-- | aosp/update_attempter_android.cc | 49 | ||||
-rw-r--r-- | aosp/update_attempter_android.h | 6 | ||||
-rw-r--r-- | aosp/update_attempter_android_unittest.cc | 2 |
10 files changed, 330 insertions, 37 deletions
@@ -332,6 +332,7 @@ cc_defaults { "PlatformProperties", ], shared_libs: [ + "apex_aidl_interface-cpp", "libandroid_net", "libbase", "libbinder", @@ -367,6 +368,7 @@ cc_library_static { 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 @@ cc_test { 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 00000000..cdbc9834 --- /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 00000000..aac1cd9c --- /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 00000000..3a99f79b --- /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 00000000..c3399b61 --- /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 9bdd1750..fc89d73e 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 @@ bool DaemonStateAndroid::Initialize() { 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 3cbc0c7f..bf015c94 100644 --- a/aosp/sideload_main.cc +++ b/aosp/sideload_main.cc @@ -154,8 +154,11 @@ bool ApplyUpdatePayload(const string& payload, 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 5523a581..c685855f 100644 --- a/aosp/update_attempter_android.cc +++ b/aosp/update_attempter_android.cc @@ -71,9 +71,6 @@ using update_engine::UpdateEngineStatus; 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 @@ UpdateAttempterAndroid::UpdateAttempterAndroid( 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 @@ BootControlInterface::Slot UpdateAttempterAndroid::GetTargetSlot() const { 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 @@ uint64_t UpdateAttempterAndroid::AllocateSpaceForPayload( 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 @@ uint64_t UpdateAttempterAndroid::AllocateSpaceForPayload( 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 499f8f6b..70938bcd 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 @@ class UpdateAttempterAndroid 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 @@ class UpdateAttempterAndroid 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 173e943d..f799df3e 100644 --- a/aosp/update_attempter_android_unittest.cc +++ b/aosp/update_attempter_android_unittest.cc @@ -69,7 +69,7 @@ class UpdateAttempterAndroidTest : public ::testing::Test { 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_; |