Merge "Lock down String8.isEmpty() usage" into main
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 5393e25..267571b 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -12,6 +12,7 @@
"-Wno-unused-argument",
"-Wno-unused-function",
"-Wno-nullability-completeness",
+ "-Wno-reorder-init-list",
"-Os",
"-fno-finite-loops",
"-DANDROID_DEBUGGABLE=0",
diff --git a/fastboot/README.md b/fastboot/README.md
index 55583eb..28e623c 100644
--- a/fastboot/README.md
+++ b/fastboot/README.md
@@ -188,6 +188,21 @@
erase %s Erase a given partition (can only be used in conjunction)
with if-wipe -> eg. if-wipe erase cache
+Flashing Optimization:
+
+ After generating the list of tasks to execute, Fastboot will try and
+ optimize the flashing of the dynamic partitions by constructing an
+ optimized flash super task. Fastboot will explicitly pattern match the
+ following commands and try and concatenate it into this task. (doing so
+ will allow us to avoid the reboot into userspace fastbootd which takes
+ significant time)
+
+ //Optimizable Block
+ reboot fastboot
+ update-super ---> generate optimized flash super task
+ $FOR EACH {dynamic partition}
+ flash {dynamic partition}
+
## Client Variables
The "getvar:%s" command is used to read client variables which
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index d605393..6de598f 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -639,6 +639,12 @@
return UpdateSuper(device, args[1], wipe);
}
+static bool IsLockedDsu() {
+ std::string active_dsu;
+ android::gsi::GetActiveDsu(&active_dsu);
+ return android::base::EndsWith(active_dsu, ".lock");
+}
+
bool GsiHandler(FastbootDevice* device, const std::vector<std::string>& args) {
if (args.size() != 2) {
return device->WriteFail("Invalid arguments");
@@ -653,6 +659,11 @@
return device->WriteStatus(FastbootResult::FAIL, "No GSI is installed");
}
+ if ((args[1] == "wipe" || args[1] == "disable") && GetDeviceLockStatus() && IsLockedDsu()) {
+ // Block commands that modify the states of locked DSU
+ return device->WriteFail("Command not available on locked DSU/devices");
+ }
+
if (args[1] == "wipe") {
if (!android::gsi::UninstallGsi()) {
return device->WriteStatus(FastbootResult::FAIL, strerror(errno));
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index d7e3db6..21df729 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -1286,7 +1286,7 @@
return current_slot;
}
-static int get_slot_count() {
+static int get_slot_count(fastboot::IFastBootDriver* fb) {
std::string var;
int count = 0;
if (fb->GetVar("slot-count", &var) != fastboot::SUCCESS ||
@@ -1296,8 +1296,8 @@
return count;
}
-bool supports_AB() {
- return get_slot_count() >= 2;
+bool supports_AB(fastboot::IFastBootDriver* fb) {
+ return get_slot_count(fb) >= 2;
}
// Given a current slot, this returns what the 'other' slot is.
@@ -1309,7 +1309,7 @@
}
static std::string get_other_slot(const std::string& current_slot) {
- return get_other_slot(current_slot, get_slot_count());
+ return get_other_slot(current_slot, get_slot_count(fb));
}
static std::string get_other_slot(int count) {
@@ -1317,7 +1317,7 @@
}
static std::string get_other_slot() {
- return get_other_slot(get_current_slot(), get_slot_count());
+ return get_other_slot(get_current_slot(), get_slot_count(fb));
}
static std::string verify_slot(const std::string& slot_name, bool allow_all) {
@@ -1326,7 +1326,7 @@
if (allow_all) {
return "all";
} else {
- int count = get_slot_count();
+ int count = get_slot_count(fb);
if (count > 0) {
return "a";
} else {
@@ -1335,7 +1335,7 @@
}
}
- int count = get_slot_count();
+ int count = get_slot_count(fb);
if (count == 0) die("Device does not support slots");
if (slot == "other") {
@@ -1408,7 +1408,7 @@
slot.c_str());
}
if (has_slot == "yes") {
- for (int i = 0; i < get_slot_count(); i++) {
+ for (int i = 0; i < get_slot_count(fb); i++) {
do_for_partition(part, std::string(1, (char)(i + 'a')), func, force_slot);
}
} else {
@@ -1529,7 +1529,7 @@
// Sets slot_override as the active slot. If slot_override is blank,
// set current slot as active instead. This clears slot-unbootable.
static void set_active(const std::string& slot_override) {
- if (!supports_AB()) return;
+ if (!supports_AB(fb)) return;
if (slot_override != "") {
fb->SetActive(slot_override);
@@ -1844,7 +1844,7 @@
fp_->secondary_slot = get_other_slot();
}
if (fp_->secondary_slot == "") {
- if (supports_AB()) {
+ if (supports_AB(fb)) {
fprintf(stderr, "Warning: Could not determine slot for secondary images. Ignoring.\n");
}
fp_->skip_secondary = true;
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index 75b8d29..35deea7 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -29,10 +29,8 @@
#include <functional>
#include <string>
-#include "fastboot_driver.h"
#include "fastboot_driver_interface.h"
#include "filesystem.h"
-#include "super_flash_helper.h"
#include "task.h"
#include "util.h"
@@ -183,12 +181,12 @@
};
Result<NetworkSerial, FastbootError> ParseNetworkSerial(const std::string& serial);
-bool supports_AB();
std::string GetPartitionName(const ImageEntry& entry, const std::string& current_slot_);
void flash_partition_files(const std::string& partition, const std::vector<SparsePtr>& files);
int64_t get_sparse_limit(int64_t size, const FlashingPlan* fp);
std::vector<SparsePtr> resparse_file(sparse_file* s, int64_t max_size);
+bool supports_AB(fastboot::IFastBootDriver* fb);
bool is_retrofit_device(fastboot::IFastBootDriver* fb);
bool is_logical(const std::string& partition);
void fb_perform_format(const std::string& partition, int skip_if_not_supported,
diff --git a/fastboot/fastboot_driver.h b/fastboot/fastboot_driver.h
index 30298cb..49cebd9 100644
--- a/fastboot/fastboot_driver.h
+++ b/fastboot/fastboot_driver.h
@@ -27,7 +27,6 @@
*/
#pragma once
#include <cstdlib>
-#include <deque>
#include <functional>
#include <limits>
#include <memory>
@@ -38,10 +37,8 @@
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
#include <bootimg.h>
-#include <inttypes.h>
#include <sparse/sparse.h>
-#include "constants.h"
#include "fastboot_driver_interface.h"
#include "transport.h"
diff --git a/fastboot/fuzzy_fastboot/main.cpp b/fastboot/fuzzy_fastboot/main.cpp
index 43aaec9..79f3939 100644
--- a/fastboot/fuzzy_fastboot/main.cpp
+++ b/fastboot/fuzzy_fastboot/main.cpp
@@ -50,6 +50,7 @@
#include <gtest/gtest.h>
#include <sparse/sparse.h>
+#include "constants.h"
#include "fastboot_driver.h"
#include "usb.h"
@@ -928,8 +929,7 @@
ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
std::string resp;
- EXPECT_EQ(fb->GetVar("product", &resp), SUCCESS)
- << "Device is unresponsive to getvar command";
+ EXPECT_EQ(fb->GetVar("product", &resp), SUCCESS) << "Device is unresponsive to getvar command";
}
TEST_F(Fuzz, CommandTooLarge) {
@@ -985,11 +985,10 @@
TEST_F(Fuzz, SparseZeroBlkSize) {
// handcrafted malform sparse file with zero as block size
const std::vector<char> buf = {
- '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c', '\x00',
- '\x00', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00',
- '\x00', '\x00', '\x00', '\x00', '\xc2', '\xca', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00',
- '\x10', '\x00', '\x00', '\x00', '\x11', '\x22', '\x33', '\x44'
- };
+ '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00', '\x01', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xc2', '\xca', '\x00', '\x00', '\x01',
+ '\x00', '\x00', '\x00', '\x10', '\x00', '\x00', '\x00', '\x11', '\x22', '\x33', '\x44'};
ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
@@ -1004,13 +1003,10 @@
TEST_F(Fuzz, SparseVeryLargeBlkSize) {
// handcrafted sparse file with block size of ~4GB and divisible 4
const std::vector<char> buf = {
- '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00',
- '\x1c', '\x00', '\x0c', '\x00', '\xF0', '\xFF', '\xFF', '\xFF',
- '\x01', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00',
- '\x00', '\x00', '\x00', '\x00', '\xc3', '\xca', '\x00', '\x00',
- '\x01', '\x00', '\x00', '\x00', '\x0c', '\x00', '\x00', '\x00',
- '\x11', '\x22', '\x33', '\x44'
- };
+ '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c',
+ '\x00', '\xF0', '\xFF', '\xFF', '\xFF', '\x01', '\x00', '\x00', '\x00', '\x01', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xc3', '\xca', '\x00', '\x00', '\x01',
+ '\x00', '\x00', '\x00', '\x0c', '\x00', '\x00', '\x00', '\x11', '\x22', '\x33', '\x44'};
ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
@@ -1021,11 +1017,10 @@
TEST_F(Fuzz, SparseTrimmed) {
// handcrafted malform sparse file which is trimmed
const std::vector<char> buf = {
- '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c', '\x00',
- '\x00', '\x10', '\x00', '\x00', '\x00', '\x00', '\x08', '\x00', '\x01', '\x00', '\x00', '\x00',
- '\x00', '\x00', '\x00', '\x00', '\xc1', '\xca', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00',
- '\x00', '\x00', '\x00', '\x80', '\x11', '\x22', '\x33', '\x44'
- };
+ '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c',
+ '\x00', '\x00', '\x10', '\x00', '\x00', '\x00', '\x00', '\x08', '\x00', '\x01', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xc1', '\xca', '\x00', '\x00', '\x01',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x80', '\x11', '\x22', '\x33', '\x44'};
ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
@@ -1040,11 +1035,10 @@
TEST_F(Fuzz, SparseInvalidChurk) {
// handcrafted malform sparse file with invalid churk
const std::vector<char> buf = {
- '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c', '\x00',
- '\x00', '\x10', '\x00', '\x00', '\x00', '\x00', '\x08', '\x00', '\x01', '\x00', '\x00', '\x00',
- '\x00', '\x00', '\x00', '\x00', '\xc1', '\xca', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00',
- '\x10', '\x00', '\x00', '\x00', '\x11', '\x22', '\x33', '\x44'
- };
+ '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c',
+ '\x00', '\x00', '\x10', '\x00', '\x00', '\x00', '\x00', '\x08', '\x00', '\x01', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xc1', '\xca', '\x00', '\x00', '\x01',
+ '\x00', '\x00', '\x00', '\x10', '\x00', '\x00', '\x00', '\x11', '\x22', '\x33', '\x44'};
ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
@@ -1894,7 +1888,8 @@
if (!fastboot::FastBootTest::IsFastbootOverTcp()) {
printf("<Waiting for Device>\n");
const auto matcher = [](usb_ifc_info* info) -> int {
- return fastboot::FastBootTest::MatchFastboot(info, fastboot::FastBootTest::device_serial);
+ return fastboot::FastBootTest::MatchFastboot(info,
+ fastboot::FastBootTest::device_serial);
};
std::unique_ptr<Transport> transport;
while (!transport) {
diff --git a/fastboot/task.cpp b/fastboot/task.cpp
index f0eed0c..f13dd55 100644
--- a/fastboot/task.cpp
+++ b/fastboot/task.cpp
@@ -15,8 +15,7 @@
//
#include "task.h"
-#include <cstddef>
-#include <iostream>
+#include "fastboot_driver.h"
#include <android-base/logging.h>
#include <android-base/parseint.h>
@@ -130,6 +129,7 @@
// Send the data to the device.
flash_partition_files(super_name_, files);
}
+
std::string OptimizedFlashSuperTask::ToString() const {
return "optimized-flash-super";
}
@@ -165,7 +165,7 @@
LOG(INFO) << "super optimization is disabled";
return nullptr;
}
- if (!supports_AB()) {
+ if (!supports_AB(fp->fb)) {
LOG(VERBOSE) << "Cannot optimize flashing super on non-AB device";
return nullptr;
}
@@ -218,17 +218,21 @@
auto s = helper->GetSparseLayout();
if (!s) return nullptr;
- // Remove images that we already flashed, just in case we have non-dynamic OS images.
+
+ // Remove tasks that are concatenated into this optimized task
auto remove_if_callback = [&](const auto& task) -> bool {
if (auto flash_task = task->AsFlashTask()) {
return helper->WillFlash(flash_task->GetPartitionAndSlot());
} else if (auto update_super_task = task->AsUpdateSuperTask()) {
return true;
} else if (auto reboot_task = task->AsRebootTask()) {
- return true;
+ if (reboot_task->GetTarget() == "fastboot") {
+ return true;
+ }
}
return false;
};
+
tasks.erase(std::remove_if(tasks.begin(), tasks.end(), remove_if_callback), tasks.end());
return std::make_unique<OptimizedFlashSuperTask>(super_name, std::move(helper), std::move(s),
diff --git a/fastboot/task.h b/fastboot/task.h
index 6ebe381..a98c874 100644
--- a/fastboot/task.h
+++ b/fastboot/task.h
@@ -15,10 +15,8 @@
//
#pragma once
-#include <sstream>
#include <string>
-#include "fastboot_driver.h"
#include "super_flash_helper.h"
#include "util.h"
@@ -29,6 +27,7 @@
class FlashTask;
class RebootTask;
class UpdateSuperTask;
+class OptimizedFlashSuperTask;
class WipeTask;
class ResizeTask;
class Task {
@@ -40,6 +39,7 @@
virtual FlashTask* AsFlashTask() { return nullptr; }
virtual RebootTask* AsRebootTask() { return nullptr; }
virtual UpdateSuperTask* AsUpdateSuperTask() { return nullptr; }
+ virtual OptimizedFlashSuperTask* AsOptimizedFlashSuperTask() { return nullptr; }
virtual WipeTask* AsWipeTask() { return nullptr; }
virtual ResizeTask* AsResizeTask() { return nullptr; }
@@ -86,13 +86,13 @@
public:
OptimizedFlashSuperTask(const std::string& super_name, std::unique_ptr<SuperFlashHelper> helper,
SparsePtr sparse_layout, uint64_t super_size, const FlashingPlan* fp);
+ virtual OptimizedFlashSuperTask* AsOptimizedFlashSuperTask() override { return this; }
static std::unique_ptr<OptimizedFlashSuperTask> Initialize(
const FlashingPlan* fp, std::vector<std::unique_ptr<Task>>& tasks);
static bool CanOptimize(const ImageSource* source,
const std::vector<std::unique_ptr<Task>>& tasks);
- using ImageEntry = std::pair<const Image*, std::string>;
void Run() override;
std::string ToString() const override;
diff --git a/fastboot/task_test.cpp b/fastboot/task_test.cpp
index 1ba3f4a..1e25b6f 100644
--- a/fastboot/task_test.cpp
+++ b/fastboot/task_test.cpp
@@ -19,11 +19,10 @@
#include "fastboot_driver_mock.h"
#include <gtest/gtest.h>
-#include <fstream>
#include <iostream>
#include <memory>
-#include <unordered_map>
#include "android-base/strings.h"
+#include "gmock/gmock.h"
using android::base::Split;
using testing::_;
@@ -235,3 +234,141 @@
<< "size of fastboot-info task list: " << fastboot_info_tasks.size()
<< " size of hardcoded task list: " << hardcoded_tasks.size();
}
+TEST_F(ParseTest, IsDynamicParitiontest) {
+ if (!get_android_product_out()) {
+ GTEST_SKIP();
+ }
+
+ LocalImageSource s;
+ fp->source = &s;
+
+ fastboot::MockFastbootDriver fb;
+ fp->fb = &fb;
+ fp->should_optimize_flash_super = true;
+ fp->should_use_fastboot_info = true;
+
+ std::vector<std::pair<std::string, bool>> test_cases = {
+ {"flash boot", false},
+ {"flash init_boot", false},
+ {"flash --apply-vbmeta vbmeta", false},
+ {"flash product", true},
+ {"flash system", true},
+ {"flash --slot-other system system_other.img", true},
+ };
+ for (auto& test : test_cases) {
+ std::unique_ptr<Task> task =
+ ParseFastbootInfoLine(fp.get(), android::base::Tokenize(test.first, " "));
+ auto flash_task = task->AsFlashTask();
+ ASSERT_FALSE(flash_task == nullptr);
+ ASSERT_EQ(FlashTask::IsDynamicParitition(fp->source, flash_task), test.second);
+ }
+}
+
+TEST_F(ParseTest, CanOptimizeTest) {
+ if (!get_android_product_out()) {
+ GTEST_SKIP();
+ }
+
+ LocalImageSource s;
+ fp->source = &s;
+ fp->sparse_limit = std::numeric_limits<int64_t>::max();
+
+ fastboot::MockFastbootDriver fb;
+ fp->fb = &fb;
+ fp->should_optimize_flash_super = false;
+ fp->should_use_fastboot_info = true;
+
+ std::vector<std::pair<std::vector<std::string>, bool>> patternmatchtest = {
+ {{"flash boot", "flash init_boot", "flash vendor_boot", "reboot fastboot",
+ "update-super", "flash product", "flash system", "flash system_ext", "flash odm",
+ "if-wipe erase userdata"},
+ true},
+ {{"flash boot", "flash init_boot", "flash vendor_boot", "reboot fastboot",
+ "update-super", "flash product", "flash system", "flash system_ext", "flash odm",
+ "if-wipe erase userdata"},
+ true},
+ {{"flash boot", "flash init_boot", "flash vendor_boot", "reboot fastboot",
+ "flash product", "flash system", "flash system_ext", "flash odm",
+ "if-wipe erase userdata"},
+ false},
+ {{"flash boot", "flash init_boot", "flash vendor_boot", "update-super", "flash product",
+ "flash system", "flash system_ext", "flash odm", "if-wipe erase userdata"},
+ false},
+ };
+
+ auto remove_if_callback = [&](const auto& task) -> bool { return !!task->AsResizeTask(); };
+
+ for (auto& test : patternmatchtest) {
+ std::vector<std::unique_ptr<Task>> tasks = ParseFastbootInfo(fp.get(), test.first);
+ tasks.erase(std::remove_if(tasks.begin(), tasks.end(), remove_if_callback), tasks.end());
+ ASSERT_EQ(OptimizedFlashSuperTask::CanOptimize(fp->source, tasks), test.second);
+ }
+}
+
+// Note: this test is exclusively testing that optimized flash super pattern matches a given task
+// list and is able to optimized based on a correct sequence of tasks
+TEST_F(ParseTest, OptimizedFlashSuperPatternMatchTest) {
+ if (!get_android_product_out()) {
+ GTEST_SKIP();
+ }
+
+ LocalImageSource s;
+ fp->source = &s;
+ fp->sparse_limit = std::numeric_limits<int64_t>::max();
+
+ fastboot::MockFastbootDriver fb;
+ fp->fb = &fb;
+ fp->should_optimize_flash_super = true;
+ fp->should_use_fastboot_info = true;
+
+ ON_CALL(fb, GetVar("super-partition-name", _, _))
+ .WillByDefault(testing::Return(fastboot::BAD_ARG));
+
+ ON_CALL(fb, GetVar("slot-count", _, _))
+ .WillByDefault(testing::DoAll(testing::SetArgPointee<1>("2"),
+ testing::Return(fastboot::SUCCESS)));
+
+ ON_CALL(fb, GetVar("partition-size:super", _, _))
+ .WillByDefault(testing::DoAll(testing::SetArgPointee<1>("1000"),
+ testing::Return(fastboot::SUCCESS)));
+
+ std::vector<std::pair<std::vector<std::string>, bool>> patternmatchtest = {
+ {{"flash boot", "flash init_boot", "flash vendor_boot", "reboot fastboot",
+ "update-super", "flash product", "flash system", "flash system_ext", "flash odm",
+ "if-wipe erase userdata"},
+ true},
+ {{"flash boot", "flash init_boot", "flash vendor_boot", "reboot fastboot",
+ "update-super", "flash product", "flash system", "flash system_ext", "flash odm",
+ "if-wipe erase userdata"},
+ true},
+ {{"flash boot", "flash init_boot", "flash vendor_boot", "reboot fastboot",
+ "flash product", "flash system", "flash system_ext", "flash odm",
+ "if-wipe erase userdata"},
+ false},
+ {{"flash boot", "flash init_boot", "flash vendor_boot", "update-super", "flash product",
+ "flash system", "flash system_ext", "flash odm", "if-wipe erase userdata"},
+ false},
+ };
+
+ for (auto& test : patternmatchtest) {
+ std::vector<std::unique_ptr<Task>> tasks = ParseFastbootInfo(fp.get(), test.first);
+ // Check to make sure we have an optimized flash super task && no more dynamic partition
+ // flashing tasks
+ auto&& IsOptimized = [](const FlashingPlan* fp,
+ const std::vector<std::unique_ptr<Task>>& tasks) {
+ bool contains_optimized_task = false;
+ for (auto& task : tasks) {
+ if (auto optimized_task = task->AsOptimizedFlashSuperTask()) {
+ contains_optimized_task = true;
+ }
+ if (auto flash_task = task->AsFlashTask()) {
+ if (FlashTask::IsDynamicParitition(fp->source, flash_task)) {
+ return false;
+ }
+ }
+ }
+ return contains_optimized_task;
+ };
+ ASSERT_EQ(IsOptimized(fp.get(), tasks), test.second);
+ }
+}
diff --git a/fs_mgr/fs_mgr_overlayfs_control.cpp b/fs_mgr/fs_mgr_overlayfs_control.cpp
index 68576f2..2cc0d2d 100644
--- a/fs_mgr/fs_mgr_overlayfs_control.cpp
+++ b/fs_mgr/fs_mgr_overlayfs_control.cpp
@@ -688,7 +688,7 @@
continue;
}
} else {
- if (GetEntryForMountPoint(&fstab, overlay_mount_point) == nullptr) {
+ if (!fs_mgr_overlayfs_already_mounted(overlay_mount_point, false /* overlay */)) {
continue;
}
}
diff --git a/fs_mgr/fs_mgr_overlayfs_mount.cpp b/fs_mgr/fs_mgr_overlayfs_mount.cpp
index 8fb63b1..9f17c06 100644
--- a/fs_mgr/fs_mgr_overlayfs_mount.cpp
+++ b/fs_mgr/fs_mgr_overlayfs_mount.cpp
@@ -733,15 +733,18 @@
bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point, bool overlay_only) {
Fstab fstab;
- if (!ReadFstabFromFile("/proc/mounts", &fstab)) {
+ if (!ReadFstabFromProcMounts(&fstab)) {
return false;
}
const auto lowerdir = kLowerdirOption + mount_point;
- for (const auto& entry : fstab) {
- if (overlay_only && "overlay" != entry.fs_type && "overlayfs" != entry.fs_type) continue;
- if (mount_point != entry.mount_point) continue;
- if (!overlay_only) return true;
- const auto options = android::base::Split(entry.fs_options, ",");
+ for (const auto& entry : GetEntriesForMountPoint(&fstab, mount_point)) {
+ if (!overlay_only) {
+ return true;
+ }
+ if (entry->fs_type != "overlay" && entry->fs_type != "overlayfs") {
+ continue;
+ }
+ const auto options = android::base::Split(entry->fs_options, ",");
for (const auto& opt : options) {
if (opt == lowerdir) {
return true;
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index b0f2b84..0131f73 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -476,6 +476,22 @@
header_libs: [
"libstorage_literals_headers",
],
+
+ dist: {
+ targets: [
+ "sdk",
+ "sdk-repo-platform-tools",
+ "sdk_repo",
+ ],
+ },
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ windows: {
+ enabled: false,
+ },
+ },
}
python_library_host {
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_compress.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_compress.h
index cf65615..fd12b84 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_compress.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_compress.h
@@ -24,24 +24,26 @@
class ICompressor {
public:
- explicit ICompressor(uint32_t compression_level) : compression_level_(compression_level) {}
+ explicit ICompressor(uint32_t compression_level, uint32_t block_size)
+ : compression_level_(compression_level), block_size_(block_size) {}
virtual ~ICompressor() {}
// Factory methods for compression methods.
- static std::unique_ptr<ICompressor> Gz(uint32_t compression_level);
- static std::unique_ptr<ICompressor> Brotli(uint32_t compression_level);
- static std::unique_ptr<ICompressor> Lz4(uint32_t compression_level);
+ static std::unique_ptr<ICompressor> Gz(uint32_t compression_level, const int32_t BLOCK_SZ);
+ static std::unique_ptr<ICompressor> Brotli(uint32_t compression_level, const int32_t BLOCK_SZ);
+ static std::unique_ptr<ICompressor> Lz4(uint32_t compression_level, const int32_t BLOCK_SZ);
static std::unique_ptr<ICompressor> Zstd(uint32_t compression_level, const int32_t BLOCK_SZ);
static std::unique_ptr<ICompressor> Create(CowCompression compression, const int32_t BLOCK_SZ);
uint32_t GetCompressionLevel() const { return compression_level_; }
-
+ uint32_t GetBlockSize() const { return block_size_; }
[[nodiscard]] virtual std::basic_string<uint8_t> Compress(const void* data,
size_t length) const = 0;
private:
uint32_t compression_level_;
+ uint32_t block_size_;
};
} // namespace snapshot
} // namespace android
\ No newline at end of file
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 6bc8b9b..e7b0020 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -73,6 +73,9 @@
class SnapshotMergeStats;
class SnapshotStatus;
+using std::chrono::duration_cast;
+using namespace std::chrono_literals;
+
static constexpr const std::string_view kCowGroupName = "cow";
static constexpr char kVirtualAbCompressionProp[] = "ro.virtual_ab.compression.enabled";
@@ -424,6 +427,7 @@
FRIEND_TEST(SnapshotTest, MergeFailureCode);
FRIEND_TEST(SnapshotTest, NoMergeBeforeReboot);
FRIEND_TEST(SnapshotTest, UpdateBootControlHal);
+ FRIEND_TEST(SnapshotTest, BootSnapshotWithoutSlotSwitch);
FRIEND_TEST(SnapshotUpdateTest, AddPartition);
FRIEND_TEST(SnapshotUpdateTest, ConsistencyCheckResume);
FRIEND_TEST(SnapshotUpdateTest, DaemonTransition);
@@ -436,6 +440,7 @@
FRIEND_TEST(SnapshotUpdateTest, QueryStatusError);
FRIEND_TEST(SnapshotUpdateTest, SnapshotStatusFileWithoutCow);
FRIEND_TEST(SnapshotUpdateTest, SpaceSwapUpdate);
+ FRIEND_TEST(SnapshotUpdateTest, MapAllSnapshotsWithoutSlotSwitch);
friend class SnapshotTest;
friend class SnapshotUpdateTest;
friend class FlashAfterUpdateTest;
@@ -456,7 +461,7 @@
bool EnsureImageManager();
// Ensure we're connected to snapuserd.
- bool EnsureSnapuserdConnected();
+ bool EnsureSnapuserdConnected(std::chrono::milliseconds timeout_ms = 10s);
// Helpers for first-stage init.
const std::unique_ptr<IDeviceInfo>& device() const { return device_; }
@@ -549,6 +554,16 @@
// Unmap and remove all known snapshots.
bool RemoveAllSnapshots(LockedFile* lock);
+ // Boot device off snapshots without slot switch
+ bool BootFromSnapshotsWithoutSlotSwitch();
+
+ // Remove kBootSnapshotsWithoutSlotSwitch so that device can boot
+ // without snapshots on the current slot
+ bool PrepareDeviceToBootWithoutSnapshot();
+
+ // Is the kBootSnapshotsWithoutSlotSwitch present
+ bool IsSnapshotWithoutSlotSwitch();
+
// List the known snapshot names.
bool ListSnapshots(LockedFile* lock, std::vector<std::string>* snapshots,
const std::string& suffix = "");
@@ -663,6 +678,7 @@
std::string GetRollbackIndicatorPath();
std::string GetForwardMergeIndicatorPath();
std::string GetOldPartitionMetadataPath();
+ std::string GetBootSnapshotsWithoutSlotSwitchPath();
const LpMetadata* ReadOldPartitionMetadata(LockedFile* lock);
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp
index 71ac59f..abc7d33 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp
@@ -56,16 +56,16 @@
}
std::unique_ptr<ICompressor> ICompressor::Create(CowCompression compression,
- const int32_t BLOCK_SZ) {
+ const int32_t block_size) {
switch (compression.algorithm) {
case kCowCompressLz4:
- return ICompressor::Lz4(compression.compression_level);
+ return ICompressor::Lz4(compression.compression_level, block_size);
case kCowCompressBrotli:
- return ICompressor::Brotli(compression.compression_level);
+ return ICompressor::Brotli(compression.compression_level, block_size);
case kCowCompressGz:
- return ICompressor::Gz(compression.compression_level);
+ return ICompressor::Gz(compression.compression_level, block_size);
case kCowCompressZstd:
- return ICompressor::Zstd(compression.compression_level, BLOCK_SZ);
+ return ICompressor::Zstd(compression.compression_level, block_size);
case kCowCompressNone:
return nullptr;
}
@@ -100,7 +100,8 @@
class GzCompressor final : public ICompressor {
public:
- GzCompressor(uint32_t compression_level) : ICompressor(compression_level){};
+ GzCompressor(uint32_t compression_level, const uint32_t block_size)
+ : ICompressor(compression_level, block_size){};
std::basic_string<uint8_t> Compress(const void* data, size_t length) const override {
const auto bound = compressBound(length);
@@ -120,7 +121,8 @@
class Lz4Compressor final : public ICompressor {
public:
- Lz4Compressor(uint32_t compression_level) : ICompressor(compression_level){};
+ Lz4Compressor(uint32_t compression_level, const uint32_t block_size)
+ : ICompressor(compression_level, block_size){};
std::basic_string<uint8_t> Compress(const void* data, size_t length) const override {
const auto bound = LZ4_compressBound(length);
@@ -151,7 +153,8 @@
class BrotliCompressor final : public ICompressor {
public:
- BrotliCompressor(uint32_t compression_level) : ICompressor(compression_level){};
+ BrotliCompressor(uint32_t compression_level, const uint32_t block_size)
+ : ICompressor(compression_level, block_size){};
std::basic_string<uint8_t> Compress(const void* data, size_t length) const override {
const auto bound = BrotliEncoderMaxCompressedSize(length);
@@ -176,10 +179,11 @@
class ZstdCompressor final : public ICompressor {
public:
- ZstdCompressor(uint32_t compression_level, const uint32_t MAX_BLOCK_SIZE)
- : ICompressor(compression_level), zstd_context_(ZSTD_createCCtx(), ZSTD_freeCCtx) {
+ ZstdCompressor(uint32_t compression_level, const uint32_t block_size)
+ : ICompressor(compression_level, block_size),
+ zstd_context_(ZSTD_createCCtx(), ZSTD_freeCCtx) {
ZSTD_CCtx_setParameter(zstd_context_.get(), ZSTD_c_compressionLevel, compression_level);
- ZSTD_CCtx_setParameter(zstd_context_.get(), ZSTD_c_windowLog, log2(MAX_BLOCK_SIZE));
+ ZSTD_CCtx_setParameter(zstd_context_.get(), ZSTD_c_windowLog, log2(GetBlockSize()));
};
std::basic_string<uint8_t> Compress(const void* data, size_t length) const override {
@@ -313,20 +317,23 @@
return true;
}
-std::unique_ptr<ICompressor> ICompressor::Brotli(uint32_t compression_level) {
- return std::make_unique<BrotliCompressor>(compression_level);
+std::unique_ptr<ICompressor> ICompressor::Brotli(uint32_t compression_level,
+ const int32_t block_size) {
+ return std::make_unique<BrotliCompressor>(compression_level, block_size);
}
-std::unique_ptr<ICompressor> ICompressor::Gz(uint32_t compression_level) {
- return std::make_unique<GzCompressor>(compression_level);
+std::unique_ptr<ICompressor> ICompressor::Gz(uint32_t compression_level, const int32_t block_size) {
+ return std::make_unique<GzCompressor>(compression_level, block_size);
}
-std::unique_ptr<ICompressor> ICompressor::Lz4(uint32_t compression_level) {
- return std::make_unique<Lz4Compressor>(compression_level);
+std::unique_ptr<ICompressor> ICompressor::Lz4(uint32_t compression_level,
+ const int32_t block_size) {
+ return std::make_unique<Lz4Compressor>(compression_level, block_size);
}
-std::unique_ptr<ICompressor> ICompressor::Zstd(uint32_t compression_level, const int32_t BLOCK_SZ) {
- return std::make_unique<ZstdCompressor>(compression_level, BLOCK_SZ);
+std::unique_ptr<ICompressor> ICompressor::Zstd(uint32_t compression_level,
+ const int32_t block_size) {
+ return std::make_unique<ZstdCompressor>(compression_level, block_size);
}
void CompressWorker::Finalize() {
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 51389a0..4743a42 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -83,6 +83,8 @@
using namespace std::chrono_literals;
using namespace std::string_literals;
+static constexpr char kBootSnapshotsWithoutSlotSwitch[] =
+ "/metadata/ota/snapshot-boot-without-slot-switch";
static constexpr char kBootIndicatorPath[] = "/metadata/ota/snapshot-boot";
static constexpr char kRollbackIndicatorPath[] = "/metadata/ota/rollback-indicator";
static constexpr auto kUpdateStateCheckInterval = 2s;
@@ -217,6 +219,12 @@
auto file = LockExclusive();
if (!file) return false;
+ if (IsSnapshotWithoutSlotSwitch()) {
+ LOG(ERROR) << "Cannot cancel the snapshots as partitions are mounted off the snapshots on "
+ "current slot.";
+ return false;
+ }
+
UpdateState state = ReadUpdateState(file.get());
if (state == UpdateState::None) {
RemoveInvalidSnapshots(file.get());
@@ -299,10 +307,9 @@
// - For ForwardMerge, FinishedSnapshotWrites asserts that the existence of the indicator
// matches the incoming update.
std::vector<std::string> files = {
- GetSnapshotBootIndicatorPath(),
- GetRollbackIndicatorPath(),
- GetForwardMergeIndicatorPath(),
- GetOldPartitionMetadataPath(),
+ GetSnapshotBootIndicatorPath(), GetRollbackIndicatorPath(),
+ GetForwardMergeIndicatorPath(), GetOldPartitionMetadataPath(),
+ GetBootSnapshotsWithoutSlotSwitchPath(),
};
for (const auto& file : files) {
RemoveFileIfExists(file);
@@ -483,6 +490,32 @@
LOG(ERROR) << "Failed to retrieve base_sectors from Snapuserd";
return false;
}
+ } else if (IsSnapshotWithoutSlotSwitch()) {
+ // When snapshots are on current slot, we determine the size
+ // of block device based on the number of COW operations. We cannot
+ // use base device as it will be from older image.
+ size_t num_ops = 0;
+ uint64_t dev_sz = 0;
+ unique_fd fd(open(cow_file.c_str(), O_RDONLY | O_CLOEXEC));
+ if (fd < 0) {
+ PLOG(ERROR) << "Failed to open " << cow_file;
+ return false;
+ }
+
+ CowReader reader;
+ if (!reader.Parse(std::move(fd))) {
+ LOG(ERROR) << "Failed to parse cow " << cow_file;
+ return false;
+ }
+
+ const auto& header = reader.GetHeader();
+ if (header.prefix.major_version > 2) {
+ LOG(ERROR) << "COW format not supported";
+ return false;
+ }
+ num_ops = reader.get_num_total_data_ops();
+ dev_sz = (num_ops * header.block_size);
+ base_sectors = dev_sz >> 9;
} else {
// For userspace snapshots, the size of the base device is taken as the
// size of the dm-user block device. Since there is no pseudo mapping
@@ -1479,6 +1512,10 @@
return result;
}
+std::string SnapshotManager::GetBootSnapshotsWithoutSlotSwitchPath() {
+ return metadata_dir_ + "/" + android::base::Basename(kBootSnapshotsWithoutSlotSwitch);
+}
+
std::string SnapshotManager::GetSnapshotBootIndicatorPath() {
return metadata_dir_ + "/" + android::base::Basename(kBootIndicatorPath);
}
@@ -2120,6 +2157,10 @@
return state;
}
+bool SnapshotManager::IsSnapshotWithoutSlotSwitch() {
+ return (access(GetBootSnapshotsWithoutSlotSwitchPath().c_str(), F_OK) == 0);
+}
+
bool SnapshotManager::UpdateUsesCompression() {
auto lock = LockShared();
if (!lock) return false;
@@ -2212,6 +2253,13 @@
}
bool SnapshotManager::NeedSnapshotsInFirstStageMount() {
+ if (IsSnapshotWithoutSlotSwitch()) {
+ if (GetCurrentSlot() != Slot::Source) {
+ LOG(ERROR) << "Snapshots marked to boot without slot switch; but slot is wrong";
+ return false;
+ }
+ return true;
+ }
// If we fail to read, we'll wind up using CreateLogicalPartitions, which
// will create devices that look like the old slot, except with extra
// content at the end of each device. This will confuse dm-verity, and
@@ -2347,7 +2395,8 @@
// completed, live_snapshot_status is set to nullopt.
std::optional<SnapshotStatus> live_snapshot_status;
do {
- if (!(params.partition->attributes & LP_PARTITION_ATTR_UPDATED)) {
+ if (!IsSnapshotWithoutSlotSwitch() &&
+ !(params.partition->attributes & LP_PARTITION_ATTR_UPDATED)) {
LOG(INFO) << "Detected re-flashing of partition, will skip snapshot: "
<< params.GetPartitionName();
break;
@@ -2703,7 +2752,7 @@
// to unmap; hence, we can't be deleting the device
// as the table would be mounted off partitions and will fail.
if (snapshot_status.state() != SnapshotState::MERGE_COMPLETED) {
- if (!DeleteDeviceIfExists(dm_user_name)) {
+ if (!DeleteDeviceIfExists(dm_user_name, 4000ms)) {
LOG(ERROR) << "Cannot unmap " << dm_user_name;
return false;
}
@@ -3098,7 +3147,7 @@
return true;
}
-bool SnapshotManager::EnsureSnapuserdConnected() {
+bool SnapshotManager::EnsureSnapuserdConnected(std::chrono::milliseconds timeout_ms) {
if (snapuserd_client_) {
return true;
}
@@ -3107,7 +3156,7 @@
return false;
}
- snapuserd_client_ = SnapuserdClient::Connect(kSnapuserdSocket, 10s);
+ snapuserd_client_ = SnapuserdClient::Connect(kSnapuserdSocket, timeout_ms);
if (!snapuserd_client_) {
LOG(ERROR) << "Unable to connect to snapuserd";
return false;
@@ -4372,13 +4421,70 @@
bool SnapshotManager::IsUserspaceSnapshotUpdateInProgress() {
auto slot = GetCurrentSlot();
if (slot == Slot::Target) {
+ // Merge in-progress
if (IsSnapuserdRequired()) {
return true;
}
}
+ // Let's check more deeper to see if snapshots are mounted
+ auto lock = LockExclusive();
+ if (!lock) {
+ return false;
+ }
+
+ std::vector<std::string> snapshots;
+ if (!ListSnapshots(lock.get(), &snapshots)) {
+ return false;
+ }
+
+ for (const auto& snapshot : snapshots) {
+ // Active snapshot and daemon is alive
+ if (IsSnapshotDevice(snapshot) && EnsureSnapuserdConnected(2s)) {
+ return true;
+ }
+ }
+
return false;
}
+bool SnapshotManager::BootFromSnapshotsWithoutSlotSwitch() {
+ auto lock = LockExclusive();
+ if (!lock) return false;
+
+ auto contents = device_->GetSlotSuffix();
+ // This is the indicator which tells first-stage init
+ // to boot from snapshots even though there was no slot-switch
+ auto boot_file = GetBootSnapshotsWithoutSlotSwitchPath();
+ if (!WriteStringToFileAtomic(contents, boot_file)) {
+ PLOG(ERROR) << "write failed: " << boot_file;
+ return false;
+ }
+
+ SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock.get());
+ update_status.set_state(UpdateState::Initiated);
+ update_status.set_userspace_snapshots(true);
+ update_status.set_using_snapuserd(true);
+ if (!WriteSnapshotUpdateStatus(lock.get(), update_status)) {
+ return false;
+ }
+ return true;
+}
+
+bool SnapshotManager::PrepareDeviceToBootWithoutSnapshot() {
+ auto lock = LockExclusive();
+ if (!lock) return false;
+
+ android::base::RemoveFileIfExists(GetSnapshotBootIndicatorPath());
+ android::base::RemoveFileIfExists(GetBootSnapshotsWithoutSlotSwitchPath());
+
+ SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock.get());
+ update_status.set_state(UpdateState::Cancelled);
+ if (!WriteSnapshotUpdateStatus(lock.get(), update_status)) {
+ return false;
+ }
+ return true;
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 3b6d26a..e506110 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -2559,6 +2559,56 @@
}
}
+TEST_F(SnapshotUpdateTest, MapAllSnapshotsWithoutSlotSwitch) {
+ MountMetadata();
+ AddOperationForPartitions();
+ // Execute the update.
+ ASSERT_TRUE(sm->BeginUpdate());
+ ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+
+ if (!sm->UpdateUsesUserSnapshots()) {
+ GTEST_SKIP() << "Test does not apply as UserSnapshots aren't enabled.";
+ }
+
+ ASSERT_TRUE(WriteSnapshots());
+ ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
+
+ if (ShouldSkipLegacyMerging()) {
+ GTEST_SKIP() << "Skipping legacy merge test";
+ }
+ // Mark the indicator
+ ASSERT_TRUE(sm->BootFromSnapshotsWithoutSlotSwitch());
+
+ ASSERT_TRUE(sm->EnsureSnapuserdConnected());
+ sm->set_use_first_stage_snapuserd(true);
+
+ ASSERT_TRUE(sm->NeedSnapshotsInFirstStageMount());
+
+ // Map snapshots
+ ASSERT_TRUE(sm->MapAllSnapshots(10s));
+
+ // New updates should fail
+ ASSERT_FALSE(sm->BeginUpdate());
+
+ // Snapshots cannot be cancelled
+ ASSERT_FALSE(sm->CancelUpdate());
+
+ // Merge cannot start
+ ASSERT_FALSE(sm->InitiateMerge());
+
+ // Read bytes back and verify they match the cache.
+ ASSERT_TRUE(IsPartitionUnchanged("sys_b"));
+
+ // Remove the indicators
+ ASSERT_TRUE(sm->PrepareDeviceToBootWithoutSnapshot());
+
+ // Ensure snapshots are still mounted
+ ASSERT_TRUE(sm->IsUserspaceSnapshotUpdateInProgress());
+
+ // Cleanup snapshots
+ ASSERT_TRUE(sm->UnmapAllSnapshots());
+}
+
TEST_F(SnapshotUpdateTest, MapAllSnapshots) {
AddOperationForPartitions();
// Execute the update.
diff --git a/fs_mgr/libsnapshot/snapshotctl.cpp b/fs_mgr/libsnapshot/snapshotctl.cpp
index 1323b0b..ebaca2d 100644
--- a/fs_mgr/libsnapshot/snapshotctl.cpp
+++ b/fs_mgr/libsnapshot/snapshotctl.cpp
@@ -75,7 +75,11 @@
" unmap-snapshots\n"
" Unmap all pre-created snapshots\n"
" delete-snapshots\n"
- " Delete all pre-created snapshots\n";
+ " Delete all pre-created snapshots\n"
+ " revert-snapshots\n"
+ " Prepares devices to boot without snapshots on next boot.\n"
+ " This does not delete the snapshot. It only removes the indicators\n"
+ " so that first stage init will not mount from snapshots.\n";
return EX_USAGE;
}
@@ -87,9 +91,11 @@
MapSnapshots(std::string path = "");
bool CreateSnapshotDevice(std::string& partition_name, std::string& patch);
bool InitiateThreadedSnapshotWrite(std::string& pname, std::string& snapshot_patch);
- bool WaitForSnapshotWritesToComplete();
+ bool FinishSnapshotWrites();
bool UnmapCowImagePath(std::string& name);
- bool DeleteCowImage(std::string& name);
+ bool DeleteSnapshots();
+ bool CleanupSnapshot() { return sm_->PrepareDeviceToBootWithoutSnapshot(); }
+ bool BeginUpdate();
private:
std::optional<std::string> GetCowImagePath(std::string& name);
@@ -107,7 +113,24 @@
exit(1);
}
snapshot_dir_path_ = path + "/";
+}
+
+bool MapSnapshots::BeginUpdate() {
lock_ = sm_->LockExclusive();
+ std::vector<std::string> snapshots;
+ sm_->ListSnapshots(lock_.get(), &snapshots);
+ if (!snapshots.empty()) {
+ // Snapshots are already present.
+ return true;
+ }
+
+ lock_ = nullptr;
+ if (!sm_->BeginUpdate()) {
+ LOG(ERROR) << "BeginUpdate failed";
+ return false;
+ }
+ lock_ = sm_->LockExclusive();
+ return true;
}
bool MapSnapshots::CreateSnapshotDevice(std::string& partition_name, std::string& patchfile) {
@@ -130,6 +153,9 @@
dev_sz &= ~(block_sz - 1);
SnapshotStatus status;
+ status.set_state(SnapshotState::CREATED);
+ status.set_using_snapuserd(true);
+ status.set_old_partition_size(0);
status.set_name(partition_name);
status.set_cow_file_size(dev_sz);
status.set_cow_partition_size(0);
@@ -216,27 +242,33 @@
return true;
}
-bool MapSnapshots::WaitForSnapshotWritesToComplete() {
+bool MapSnapshots::FinishSnapshotWrites() {
bool ret = true;
for (auto& t : threads_) {
ret = t.get() && ret;
}
+ lock_ = nullptr;
if (ret) {
LOG(INFO) << "Pre-created snapshots successfully copied";
- } else {
- LOG(ERROR) << "Snapshot copy failed";
+ if (!sm_->FinishedSnapshotWrites(false)) {
+ return false;
+ }
+ return sm_->BootFromSnapshotsWithoutSlotSwitch();
}
- return ret;
+
+ LOG(ERROR) << "Snapshot copy failed";
+ return false;
}
bool MapSnapshots::UnmapCowImagePath(std::string& name) {
return sm_->UnmapCowImage(name);
}
-bool MapSnapshots::DeleteCowImage(std::string& name) {
- if (!sm_->DeleteSnapshot(lock_.get(), name)) {
- LOG(ERROR) << "Delete snapshot failed";
+bool MapSnapshots::DeleteSnapshots() {
+ lock_ = sm_->LockExclusive();
+ if (!sm_->RemoveAllUpdateState(lock_.get())) {
+ LOG(ERROR) << "Remove All Update State failed";
return false;
}
return true;
@@ -281,7 +313,8 @@
return true;
}
-bool UnMapPrecreatedSnapshots(int, char**) {
+bool UnMapPrecreatedSnapshots(int, char** argv) {
+ android::base::InitLogging(argv, &android::base::KernelLogger);
// Make sure we are root.
if (::getuid() != 0) {
LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
@@ -302,29 +335,36 @@
return true;
}
-bool DeletePrecreatedSnapshots(int, char**) {
+bool RemovePrecreatedSnapshots(int, char** argv) {
+ android::base::InitLogging(argv, &android::base::KernelLogger);
+ // Make sure we are root.
+ if (::getuid() != 0) {
+ LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
+ return false;
+ }
+
+ MapSnapshots snapshot;
+ if (!snapshot.CleanupSnapshot()) {
+ LOG(ERROR) << "CleanupSnapshot failed";
+ return false;
+ }
+ return true;
+}
+
+bool DeletePrecreatedSnapshots(int, char** argv) {
+ android::base::InitLogging(argv, &android::base::KernelLogger);
// Make sure we are root.
if (::getuid() != 0) {
LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
return EXIT_FAILURE;
}
- std::vector<std::string> partitions;
- if (!GetVerityPartitions(partitions)) {
- return false;
- }
-
MapSnapshots snapshot;
- for (auto partition : partitions) {
- if (!snapshot.DeleteCowImage(partition)) {
- LOG(ERROR) << "DeleteCowImage failed: " << partition;
- }
- }
- return true;
+ return snapshot.DeleteSnapshots();
}
bool MapPrecreatedSnapshots(int argc, char** argv) {
- android::base::InitLogging(argv, &android::base::StderrLogger);
+ android::base::InitLogging(argv, &android::base::KernelLogger);
// Make sure we are root.
if (::getuid() != 0) {
@@ -365,6 +405,11 @@
}
MapSnapshots cow(path);
+ if (!cow.BeginUpdate()) {
+ LOG(ERROR) << "BeginUpdate failed";
+ return false;
+ }
+
for (auto& pair : partitions) {
if (!cow.CreateSnapshotDevice(pair.first, pair.second)) {
LOG(ERROR) << "CreateSnapshotDevice failed for: " << pair.first;
@@ -376,7 +421,7 @@
}
}
- return cow.WaitForSnapshotWritesToComplete();
+ return cow.FinishSnapshotWrites();
}
#ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG
@@ -508,6 +553,7 @@
{"map-snapshots", MapPrecreatedSnapshots},
{"unmap-snapshots", UnMapPrecreatedSnapshots},
{"delete-snapshots", DeletePrecreatedSnapshots},
+ {"revert-snapshots", RemovePrecreatedSnapshots},
// clang-format on
};
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
index 2b72847..4301f0e 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -430,31 +430,6 @@
// ---------------------------------------------------------------------------
// Path functions
-static void setPathName(String8& s, const char* name) {
- size_t len = strlen(name);
- char* buf = s.lockBuffer(len);
-
- memcpy(buf, name, len);
-
- // remove trailing path separator, if present
- if (len > 0 && buf[len - 1] == OS_PATH_SEPARATOR) len--;
- buf[len] = '\0';
-
- s.unlockBuffer(len);
-}
-
-String8 String8::getPathLeaf(void) const
-{
- const char* cp;
- const char*const buf = mString;
-
- cp = strrchr(buf, OS_PATH_SEPARATOR);
- if (cp == nullptr)
- return String8(*this);
- else
- return String8(cp+1);
-}
-
String8 String8::getPathDir(void) const
{
const char* cp;
@@ -467,40 +442,14 @@
return String8(str, cp - str);
}
-String8 String8::walkPath(String8* outRemains) const
-{
- const char* cp;
- const char*const str = mString;
- const char* buf = str;
-
- cp = strchr(buf, OS_PATH_SEPARATOR);
- if (cp == buf) {
- // don't include a leading '/'.
- buf = buf+1;
- cp = strchr(buf, OS_PATH_SEPARATOR);
- }
-
- if (cp == nullptr) {
- String8 res = buf != str ? String8(buf) : *this;
- if (outRemains) *outRemains = String8("");
- return res;
- }
-
- String8 res(buf, cp-buf);
- if (outRemains) *outRemains = String8(cp+1);
- return res;
-}
-
/*
* Helper function for finding the start of an extension in a pathname.
*
* Returns a pointer inside mString, or NULL if no extension was found.
*/
-char* String8::find_extension(void) const
-{
+static const char* find_extension(const char* str) {
const char* lastSlash;
const char* lastDot;
- const char* const str = mString;
// only look at the filename
lastSlash = strrchr(str, OS_PATH_SEPARATOR);
@@ -515,67 +464,16 @@
return nullptr;
// looks good, ship it
- return const_cast<char*>(lastDot);
+ return lastDot;
}
String8 String8::getPathExtension(void) const
{
- char* ext;
-
- ext = find_extension();
+ auto ext = find_extension(mString);
if (ext != nullptr)
return String8(ext);
else
return String8("");
}
-String8 String8::getBasePath(void) const
-{
- char* ext;
- const char* const str = mString;
-
- ext = find_extension();
- if (ext == nullptr)
- return String8(*this);
- else
- return String8(str, ext - str);
-}
-
-String8& String8::appendPath(const char* name)
-{
- // TODO: The test below will fail for Win32 paths. Fix later or ignore.
- if (name[0] != OS_PATH_SEPARATOR) {
- if (*name == '\0') {
- // nothing to do
- return *this;
- }
-
- size_t len = length();
- if (len == 0) {
- // no existing filename, just use the new one
- setPathName(*this, name);
- return *this;
- }
-
- // make room for oldPath + '/' + newPath
- int newlen = strlen(name);
-
- char* buf = lockBuffer(len+1+newlen);
-
- // insert a '/' if needed
- if (buf[len-1] != OS_PATH_SEPARATOR)
- buf[len++] = OS_PATH_SEPARATOR;
-
- memcpy(buf+len, name, newlen+1);
- len += newlen;
-
- unlockBuffer(len);
-
- return *this;
- } else {
- setPathName(*this, name);
- return *this;
- }
-}
-
}; // namespace android
diff --git a/libutils/String8_fuzz.cpp b/libutils/String8_fuzz.cpp
index 6f7a54f..cbce050 100644
--- a/libutils/String8_fuzz.cpp
+++ b/libutils/String8_fuzz.cpp
@@ -68,30 +68,6 @@
int start_index = dataProvider->ConsumeIntegralInRange<int>(0, str1->size());
str1->find(str2->c_str(), start_index);
},
-
- // Path handling
- [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
- str1->getBasePath();
- },
- [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
- str1->getPathExtension();
- },
- [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
- str1->getPathLeaf();
- },
- [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
- str1->getPathDir();
- },
- [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
- std::shared_ptr<android::String8> path_out_str =
- std::make_shared<android::String8>();
- str1->walkPath(path_out_str.get());
- path_out_str->clear();
- },
- [](FuzzedDataProvider* dataProvider, android::String8* str1,
- android::String8*) -> void {
- str1->appendPath(dataProvider->ConsumeBytesWithTerminator<char>(5).data());
- },
};
void fuzzFormat(FuzzedDataProvider* dataProvider, android::String8* str1, bool shouldAppend) {
diff --git a/libutils/abi-dumps/arm64/source-based/libutils.so.lsdump b/libutils/abi-dumps/arm64/source-based/libutils.so.lsdump
index 46badde..8881b44 100644
--- a/libutils/abi-dumps/arm64/source-based/libutils.so.lsdump
+++ b/libutils/abi-dumps/arm64/source-based/libutils.so.lsdump
@@ -704,9 +704,6 @@
"name" : "_ZN7android7RefBaseD2Ev"
},
{
- "name" : "_ZN7android7String810appendPathEPKc"
- },
- {
"name" : "_ZN7android7String810lockBufferEm"
},
{
@@ -1145,15 +1142,6 @@
"name" : "_ZNK7android7String810getPathDirEv"
},
{
- "name" : "_ZNK7android7String811getBasePathEv"
- },
- {
- "name" : "_ZNK7android7String811getPathLeafEv"
- },
- {
- "name" : "_ZNK7android7String814find_extensionEv"
- },
- {
"name" : "_ZNK7android7String816getPathExtensionEv"
},
{
@@ -1163,9 +1151,6 @@
"name" : "_ZNK7android7String86lengthEv"
},
{
- "name" : "_ZNK7android7String88walkPathEPS0_"
- },
- {
"name" : "_ZNK7android8String1610startsWithEPKDs"
},
{
@@ -6809,22 +6794,6 @@
"source_file" : "system/core/libutils/include/utils/RefBase.h"
},
{
- "function_name" : "android::String8::appendPath",
- "linker_set_key" : "_ZN7android7String810appendPathEPKc",
- "parameters" :
- [
- {
- "is_this_ptr" : true,
- "referenced_type" : "_ZTIPN7android7String8E"
- },
- {
- "referenced_type" : "_ZTIPKc"
- }
- ],
- "return_type" : "_ZTIRN7android7String8E",
- "source_file" : "system/core/libutils/include/utils/String8.h"
- },
- {
"function_name" : "android::String8::lockBuffer",
"linker_set_key" : "_ZN7android7String810lockBufferEm",
"parameters" :
@@ -9104,6 +9073,7 @@
"source_file" : "system/core/libutils/include/utils/RefBase.h"
},
{
+ "access" : "private",
"function_name" : "android::String8::getPathDir",
"linker_set_key" : "_ZNK7android7String810getPathDirEv",
"parameters" :
@@ -9117,46 +9087,7 @@
"source_file" : "system/core/libutils/include/utils/String8.h"
},
{
- "function_name" : "android::String8::getBasePath",
- "linker_set_key" : "_ZNK7android7String811getBasePathEv",
- "parameters" :
- [
- {
- "is_this_ptr" : true,
- "referenced_type" : "_ZTIPKN7android7String8E"
- }
- ],
- "return_type" : "_ZTIN7android7String8E",
- "source_file" : "system/core/libutils/include/utils/String8.h"
- },
- {
- "function_name" : "android::String8::getPathLeaf",
- "linker_set_key" : "_ZNK7android7String811getPathLeafEv",
- "parameters" :
- [
- {
- "is_this_ptr" : true,
- "referenced_type" : "_ZTIPKN7android7String8E"
- }
- ],
- "return_type" : "_ZTIN7android7String8E",
- "source_file" : "system/core/libutils/include/utils/String8.h"
- },
- {
"access" : "private",
- "function_name" : "android::String8::find_extension",
- "linker_set_key" : "_ZNK7android7String814find_extensionEv",
- "parameters" :
- [
- {
- "is_this_ptr" : true,
- "referenced_type" : "_ZTIPKN7android7String8E"
- }
- ],
- "return_type" : "_ZTIPc",
- "source_file" : "system/core/libutils/include/utils/String8.h"
- },
- {
"function_name" : "android::String8::getPathExtension",
"linker_set_key" : "_ZNK7android7String816getPathExtensionEv",
"parameters" :
@@ -9203,23 +9134,6 @@
"source_file" : "system/core/libutils/include/utils/String8.h"
},
{
- "function_name" : "android::String8::walkPath",
- "linker_set_key" : "_ZNK7android7String88walkPathEPS0_",
- "parameters" :
- [
- {
- "is_this_ptr" : true,
- "referenced_type" : "_ZTIPKN7android7String8E"
- },
- {
- "default_arg" : true,
- "referenced_type" : "_ZTIPN7android7String8E"
- }
- ],
- "return_type" : "_ZTIN7android7String8E",
- "source_file" : "system/core/libutils/include/utils/String8.h"
- },
- {
"function_name" : "android::String16::startsWith",
"linker_set_key" : "_ZNK7android8String1610startsWithEPKDs",
"parameters" :
diff --git a/libutils/abi-dumps/arm_arm64/source-based/libutils.so.lsdump b/libutils/abi-dumps/arm_arm64/source-based/libutils.so.lsdump
index 219c766..e8236ea 100644
--- a/libutils/abi-dumps/arm_arm64/source-based/libutils.so.lsdump
+++ b/libutils/abi-dumps/arm_arm64/source-based/libutils.so.lsdump
@@ -704,9 +704,6 @@
"name" : "_ZN7android7RefBaseD2Ev"
},
{
- "name" : "_ZN7android7String810appendPathEPKc"
- },
- {
"name" : "_ZN7android7String810lockBufferEj"
},
{
@@ -1145,15 +1142,6 @@
"name" : "_ZNK7android7String810getPathDirEv"
},
{
- "name" : "_ZNK7android7String811getBasePathEv"
- },
- {
- "name" : "_ZNK7android7String811getPathLeafEv"
- },
- {
- "name" : "_ZNK7android7String814find_extensionEv"
- },
- {
"name" : "_ZNK7android7String816getPathExtensionEv"
},
{
@@ -1163,9 +1151,6 @@
"name" : "_ZNK7android7String86lengthEv"
},
{
- "name" : "_ZNK7android7String88walkPathEPS0_"
- },
- {
"name" : "_ZNK7android8String1610startsWithEPKDs"
},
{
@@ -6805,22 +6790,6 @@
"source_file" : "system/core/libutils/include/utils/RefBase.h"
},
{
- "function_name" : "android::String8::appendPath",
- "linker_set_key" : "_ZN7android7String810appendPathEPKc",
- "parameters" :
- [
- {
- "is_this_ptr" : true,
- "referenced_type" : "_ZTIPN7android7String8E"
- },
- {
- "referenced_type" : "_ZTIPKc"
- }
- ],
- "return_type" : "_ZTIRN7android7String8E",
- "source_file" : "system/core/libutils/include/utils/String8.h"
- },
- {
"function_name" : "android::String8::lockBuffer",
"linker_set_key" : "_ZN7android7String810lockBufferEj",
"parameters" :
@@ -9100,6 +9069,7 @@
"source_file" : "system/core/libutils/include/utils/RefBase.h"
},
{
+ "access" : "private",
"function_name" : "android::String8::getPathDir",
"linker_set_key" : "_ZNK7android7String810getPathDirEv",
"parameters" :
@@ -9113,46 +9083,7 @@
"source_file" : "system/core/libutils/include/utils/String8.h"
},
{
- "function_name" : "android::String8::getBasePath",
- "linker_set_key" : "_ZNK7android7String811getBasePathEv",
- "parameters" :
- [
- {
- "is_this_ptr" : true,
- "referenced_type" : "_ZTIPKN7android7String8E"
- }
- ],
- "return_type" : "_ZTIN7android7String8E",
- "source_file" : "system/core/libutils/include/utils/String8.h"
- },
- {
- "function_name" : "android::String8::getPathLeaf",
- "linker_set_key" : "_ZNK7android7String811getPathLeafEv",
- "parameters" :
- [
- {
- "is_this_ptr" : true,
- "referenced_type" : "_ZTIPKN7android7String8E"
- }
- ],
- "return_type" : "_ZTIN7android7String8E",
- "source_file" : "system/core/libutils/include/utils/String8.h"
- },
- {
"access" : "private",
- "function_name" : "android::String8::find_extension",
- "linker_set_key" : "_ZNK7android7String814find_extensionEv",
- "parameters" :
- [
- {
- "is_this_ptr" : true,
- "referenced_type" : "_ZTIPKN7android7String8E"
- }
- ],
- "return_type" : "_ZTIPc",
- "source_file" : "system/core/libutils/include/utils/String8.h"
- },
- {
"function_name" : "android::String8::getPathExtension",
"linker_set_key" : "_ZNK7android7String816getPathExtensionEv",
"parameters" :
@@ -9199,23 +9130,6 @@
"source_file" : "system/core/libutils/include/utils/String8.h"
},
{
- "function_name" : "android::String8::walkPath",
- "linker_set_key" : "_ZNK7android7String88walkPathEPS0_",
- "parameters" :
- [
- {
- "is_this_ptr" : true,
- "referenced_type" : "_ZTIPKN7android7String8E"
- },
- {
- "default_arg" : true,
- "referenced_type" : "_ZTIPN7android7String8E"
- }
- ],
- "return_type" : "_ZTIN7android7String8E",
- "source_file" : "system/core/libutils/include/utils/String8.h"
- },
- {
"function_name" : "android::String16::startsWith",
"linker_set_key" : "_ZNK7android8String1610startsWithEPKDs",
"parameters" :
diff --git a/libutils/include/utils/String16.h b/libutils/include/utils/String16.h
index b48b907..1fa3723 100644
--- a/libutils/include/utils/String16.h
+++ b/libutils/include/utils/String16.h
@@ -24,6 +24,11 @@
#include <utils/String8.h>
#include <utils/TypeHelpers.h>
+#if __has_include(<string_view>)
+#include <string_view>
+#define HAS_STRING_VIEW
+#endif
+
// ---------------------------------------------------------------------------
namespace android {
@@ -56,9 +61,6 @@
inline const char16_t* c_str() const;
inline const char16_t* string() const;
-private:
- static inline std::string std_string(const String16& str);
-public:
size_t size() const;
inline bool empty() const;
@@ -91,6 +93,7 @@
bool startsWith(const char16_t* prefix) const;
bool contains(const char16_t* chrs) const;
+ inline bool contains(const String16& other) const;
status_t replaceAll(char16_t replaceThis,
char16_t withThis);
@@ -113,6 +116,12 @@
inline operator const char16_t*() const;
+#ifdef HAS_STRING_VIEW
+ // Implicit cast to std::u16string is not implemented on purpose - u16string_view is much
+ // lighter and if one needs, they can still create u16string from u16string_view.
+ inline operator std::u16string_view() const;
+#endif
+
// Static and non-static String16 behave the same for the users, so
// this method isn't of much use for the users. It is public for testing.
bool isStaticString() const;
@@ -249,11 +258,6 @@
return mString;
}
-inline std::string String16::std_string(const String16& str)
-{
- return std::string(String8(str).c_str());
-}
-
inline bool String16::empty() const
{
return length() == 0;
@@ -264,6 +268,11 @@
return size();
}
+inline bool String16::contains(const String16& other) const
+{
+ return contains(other.c_str());
+}
+
inline String16& String16::operator=(const String16& other)
{
setTo(other);
@@ -353,8 +362,15 @@
return mString;
}
+inline String16::operator std::u16string_view() const
+{
+ return {mString, length()};
+}
+
} // namespace android
// ---------------------------------------------------------------------------
+#undef HAS_STRING_VIEW
+
#endif // ANDROID_STRING16_H
diff --git a/libutils/include/utils/String8.h b/libutils/include/utils/String8.h
index c798762..0b92f5b 100644
--- a/libutils/include/utils/String8.h
+++ b/libutils/include/utils/String8.h
@@ -18,7 +18,6 @@
#define ANDROID_STRING8_H
#include <iostream>
-#include <string>
#include <utils/Errors.h>
#include <utils/Unicode.h>
@@ -27,6 +26,16 @@
#include <string.h> // for strcmp
#include <stdarg.h>
+#if __has_include(<string>)
+#include <string>
+#define HAS_STRING
+#endif
+
+#if __has_include(<string_view>)
+#include <string_view>
+#define HAS_STRING_VIEW
+#endif
+
// ---------------------------------------------------------------------------
namespace android {
@@ -58,10 +67,6 @@
inline const char* c_str() const;
inline const char* string() const;
-private:
- static inline std::string std_string(const String8& str);
-public:
-
inline size_t size() const;
inline size_t bytes() const;
inline bool empty() const;
@@ -112,6 +117,10 @@
inline operator const char*() const;
+#ifdef HAS_STRING_VIEW
+ inline explicit operator std::string_view() const;
+#endif
+
char* lockBuffer(size_t size);
void unlockBuffer();
status_t unlockBuffer(size_t size);
@@ -119,90 +128,24 @@
// return the index of the first byte of other in this at or after
// start, or -1 if not found
ssize_t find(const char* other, size_t start = 0) const;
+ inline ssize_t find(const String8& other, size_t start = 0) const;
// return true if this string contains the specified substring
inline bool contains(const char* other) const;
+ inline bool contains(const String8& other) const;
// removes all occurrence of the specified substring
// returns true if any were found and removed
bool removeAll(const char* other);
+ inline bool removeAll(const String8& other);
void toLower();
-
- /*
- * These methods operate on the string as if it were a path name.
- */
-
- /*
- * Get just the filename component.
- *
- * "/tmp/foo/bar.c" --> "bar.c"
- */
- String8 getPathLeaf(void) const;
-
- /*
- * Remove the last (file name) component, leaving just the directory
- * name.
- *
- * "/tmp/foo/bar.c" --> "/tmp/foo"
- * "/tmp" --> "" // ????? shouldn't this be "/" ???? XXX
- * "bar.c" --> ""
- */
- String8 getPathDir(void) const;
-
- /*
- * Retrieve the front (root dir) component. Optionally also return the
- * remaining components.
- *
- * "/tmp/foo/bar.c" --> "tmp" (remain = "foo/bar.c")
- * "/tmp" --> "tmp" (remain = "")
- * "bar.c" --> "bar.c" (remain = "")
- */
- String8 walkPath(String8* outRemains = nullptr) const;
-
- /*
- * Return the filename extension. This is the last '.' and any number
- * of characters that follow it. The '.' is included in case we
- * decide to expand our definition of what constitutes an extension.
- *
- * "/tmp/foo/bar.c" --> ".c"
- * "/tmp" --> ""
- * "/tmp/foo.bar/baz" --> ""
- * "foo.jpeg" --> ".jpeg"
- * "foo." --> ""
- */
- String8 getPathExtension(void) const;
-
- /*
- * Return the path without the extension. Rules for what constitutes
- * an extension are described in the comment for getPathExtension().
- *
- * "/tmp/foo/bar.c" --> "/tmp/foo/bar"
- */
- String8 getBasePath(void) const;
-
- /*
- * Add a component to the pathname. We guarantee that there is
- * exactly one path separator between the old path and the new.
- * If there is no existing name, we just copy the new name in.
- *
- * If leaf is a fully qualified path (i.e. starts with '/', it
- * replaces whatever was there before.
- */
- String8& appendPath(const char* leaf);
- String8& appendPath(const String8& leaf) { return appendPath(leaf.c_str()); }
-
- /*
- * Like appendPath(), but does not affect this string. Returns a new one instead.
- */
- String8 appendPathCopy(const char* leaf) const
- { String8 p(*this); p.appendPath(leaf); return p; }
- String8 appendPathCopy(const String8& leaf) const { return appendPathCopy(leaf.c_str()); }
-
private:
+ String8 getPathDir(void) const;
+ String8 getPathExtension(void) const;
+
status_t real_append(const char* other, size_t numChars);
- char* find_extension(void) const;
const char* mString;
@@ -246,11 +189,6 @@
return mString;
}
-inline std::string String8::std_string(const String8& str)
-{
- return std::string(str.c_str());
-}
-
inline size_t String8::size() const
{
return length();
@@ -271,11 +209,26 @@
return length();
}
+inline ssize_t String8::find(const String8& other, size_t start) const
+{
+ return find(other.c_str(), start);
+}
+
inline bool String8::contains(const char* other) const
{
return find(other) >= 0;
}
+inline bool String8::contains(const String8& other) const
+{
+ return contains(other.c_str());
+}
+
+inline bool String8::removeAll(const String8& other)
+{
+ return removeAll(other.c_str());
+}
+
inline String8& String8::operator=(const String8& other)
{
setTo(other);
@@ -384,8 +337,18 @@
return mString;
}
+#ifdef HAS_STRING_VIEW
+inline String8::operator std::string_view() const
+{
+ return {mString, length()};
+}
+#endif
+
} // namespace android
// ---------------------------------------------------------------------------
+#undef HAS_STRING
+#undef HAS_STRING_VIEW
+
#endif // ANDROID_STRING8_H
diff --git a/rootdir/init.zygote32.rc b/rootdir/init.zygote32.rc
index 2f0ec8a..442bd15 100644
--- a/rootdir/init.zygote32.rc
+++ b/rootdir/init.zygote32.rc
@@ -13,7 +13,7 @@
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
- onrestart restart media.tuner
+ onrestart restart --only-if-running media.tuner
onrestart restart netd
onrestart restart wificond
task_profiles ProcessCapacityHigh
diff --git a/rootdir/init.zygote64.rc b/rootdir/init.zygote64.rc
index 74a64c8..3422121 100644
--- a/rootdir/init.zygote64.rc
+++ b/rootdir/init.zygote64.rc
@@ -13,7 +13,7 @@
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
- onrestart restart media.tuner
+ onrestart restart --only-if-running media.tuner
onrestart restart netd
onrestart restart wificond
task_profiles ProcessCapacityHigh MaxPerformance
diff --git a/toolbox/getevent.c b/toolbox/getevent.c
index e2c77c3..f65bb20 100644
--- a/toolbox/getevent.c
+++ b/toolbox/getevent.c
@@ -111,8 +111,7 @@
break;
bits_size = res + 16;
bits = realloc(bits, bits_size * 2);
- if(bits == NULL)
- err(1, "failed to allocate buffer of size %d\n", (int)bits_size);
+ if (bits == NULL) err(1, "failed to allocate buffer of size %zd", bits_size);
}
res2 = 0;
switch(i) {