Merge "init.rc: disable kernel module autoloading" into rvc-dev
diff --git a/CleanSpec.mk b/CleanSpec.mk
index c84bd24..0a534a2 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -90,3 +90,4 @@
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/debug_ramdisk/product_services)
$(call add-clean-step, find $(PRODUCT_OUT) -type l -name "charger" -print0 | xargs -0 rm -f)
$(call add-clean-step, rm -f $(PRODUCT_OUT)/system/bin/adbd)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/init/snapshotctl.rc)
diff --git a/adb/Android.bp b/adb/Android.bp
index a26017f..6fd0767 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -429,7 +429,7 @@
},
}
-cc_library_static {
+cc_library {
name: "libadbd_services",
defaults: ["adbd_defaults", "host_adbd_supported"],
recovery_available: true,
@@ -464,6 +464,7 @@
"libbase",
"libcrypto",
"libcrypto_utils",
+ "libcutils_sockets",
"liblog",
],
@@ -515,6 +516,7 @@
"libadb_tls_connection",
"libadbd_auth",
"libadbd_fs",
+ "libadbd_services",
"libasyncio",
"libbase",
"libcrypto",
@@ -533,7 +535,6 @@
},
static_libs: [
- "libadbd_services",
"libcutils_sockets",
"libdiagnose_usb",
"libmdnssd",
@@ -575,10 +576,8 @@
"libcrypto_utils",
"libcutils_sockets",
"libdiagnose_usb",
- "liblog",
"libmdnssd",
"libminijail",
- "libselinux",
"libssl",
],
@@ -588,6 +587,8 @@
"libadbd_auth",
"libadbd_fs",
"libcrypto",
+ "liblog",
+ "libselinux",
],
target: {
diff --git a/adb/client/file_sync_client.cpp b/adb/client/file_sync_client.cpp
index 922f2ba..cc38926 100644
--- a/adb/client/file_sync_client.cpp
+++ b/adb/client/file_sync_client.cpp
@@ -29,6 +29,7 @@
#include <utime.h>
#include <chrono>
+#include <deque>
#include <functional>
#include <memory>
#include <sstream>
@@ -203,7 +204,7 @@
class SyncConnection {
public:
- SyncConnection() : expect_done_(false) {
+ SyncConnection() {
max = SYNC_DATA_MAX; // TODO: decide at runtime.
std::string error;
@@ -239,16 +240,6 @@
bool IsValid() { return fd >= 0; }
- bool ReceivedError(const char* from, const char* to) {
- adb_pollfd pfd = {.fd = fd.get(), .events = POLLIN};
- int rc = adb_poll(&pfd, 1, 0);
- if (rc < 0) {
- Error("failed to poll: %s", strerror(errno));
- return true;
- }
- return rc != 0;
- }
-
void NewTransfer() {
current_ledger_.Reset();
}
@@ -258,6 +249,11 @@
global_ledger_.bytes_transferred += bytes;
}
+ void RecordFileSent(std::string from, std::string to) {
+ RecordFilesTransferred(1);
+ deferred_acknowledgements_.emplace_back(std::move(from), std::move(to));
+ }
+
void RecordFilesTransferred(size_t files) {
current_ledger_.files_transferred += files;
global_ledger_.files_transferred += files;
@@ -283,39 +279,38 @@
}
}
- bool SendRequest(int id, const char* path_and_mode) {
- size_t path_length = strlen(path_and_mode);
- if (path_length > 1024) {
- Error("SendRequest failed: path too long: %zu", path_length);
+ bool SendRequest(int id, const std::string& path) {
+ if (path.length() > 1024) {
+ Error("SendRequest failed: path too long: %zu", path.length());
errno = ENAMETOOLONG;
return false;
}
// Sending header and payload in a single write makes a noticeable
// difference to "adb sync" performance.
- std::vector<char> buf(sizeof(SyncRequest) + path_length);
+ std::vector<char> buf(sizeof(SyncRequest) + path.length());
SyncRequest* req = reinterpret_cast<SyncRequest*>(&buf[0]);
req->id = id;
- req->path_length = path_length;
+ req->path_length = path.length();
char* data = reinterpret_cast<char*>(req + 1);
- memcpy(data, path_and_mode, path_length);
+ memcpy(data, path.data(), path.length());
- return WriteFdExactly(fd, &buf[0], buf.size());
+ return WriteFdExactly(fd, buf.data(), buf.size());
}
- bool SendStat(const char* path_and_mode) {
+ bool SendStat(const std::string& path) {
if (!have_stat_v2_) {
errno = ENOTSUP;
return false;
}
- return SendRequest(ID_STAT_V2, path_and_mode);
+ return SendRequest(ID_STAT_V2, path);
}
- bool SendLstat(const char* path_and_mode) {
+ bool SendLstat(const std::string& path) {
if (have_stat_v2_) {
- return SendRequest(ID_LSTAT_V2, path_and_mode);
+ return SendRequest(ID_LSTAT_V2, path);
} else {
- return SendRequest(ID_LSTAT_V1, path_and_mode);
+ return SendRequest(ID_LSTAT_V1, path);
}
}
@@ -374,7 +369,7 @@
return true;
}
- bool SendLs(const char* path) {
+ bool SendLs(const std::string& path) {
return SendRequest(have_ls_v2_ ? ID_LIST_V2 : ID_LIST_V1, path);
}
@@ -415,28 +410,26 @@
// Sending header, payload, and footer in a single write makes a huge
// difference to "adb sync" performance.
- bool SendSmallFile(const char* path_and_mode,
- const char* lpath, const char* rpath,
- unsigned mtime,
- const char* data, size_t data_length) {
- size_t path_length = strlen(path_and_mode);
- if (path_length > 1024) {
- Error("SendSmallFile failed: path too long: %zu", path_length);
+ bool SendSmallFile(const std::string& path, mode_t mode, const std::string& lpath,
+ const std::string& rpath, unsigned mtime, const char* data,
+ size_t data_length) {
+ std::string path_and_mode = android::base::StringPrintf("%s,%d", path.c_str(), mode);
+ if (path_and_mode.length() > 1024) {
+ Error("SendSmallFile failed: path too long: %zu", path_and_mode.length());
errno = ENAMETOOLONG;
return false;
}
- std::vector<char> buf(sizeof(SyncRequest) + path_length +
- sizeof(SyncRequest) + data_length +
- sizeof(SyncRequest));
+ std::vector<char> buf(sizeof(SyncRequest) + path_and_mode.length() + sizeof(SyncRequest) +
+ data_length + sizeof(SyncRequest));
char* p = &buf[0];
SyncRequest* req_send = reinterpret_cast<SyncRequest*>(p);
req_send->id = ID_SEND;
- req_send->path_length = path_length;
+ req_send->path_length = path_and_mode.length();
p += sizeof(SyncRequest);
- memcpy(p, path_and_mode, path_length);
- p += path_length;
+ memcpy(p, path_and_mode.data(), path_and_mode.size());
+ p += path_and_mode.length();
SyncRequest* req_data = reinterpret_cast<SyncRequest*>(p);
req_data->id = ID_DATA;
@@ -451,34 +444,34 @@
p += sizeof(SyncRequest);
WriteOrDie(lpath, rpath, &buf[0], (p - &buf[0]));
- expect_done_ = true;
- // RecordFilesTransferred gets called in CopyDone.
+ RecordFileSent(lpath, rpath);
RecordBytesTransferred(data_length);
ReportProgress(rpath, data_length, data_length);
return true;
}
- bool SendLargeFile(const char* path_and_mode,
- const char* lpath, const char* rpath,
- unsigned mtime) {
+ bool SendLargeFile(const std::string& path, mode_t mode, const std::string& lpath,
+ const std::string& rpath, unsigned mtime) {
+ std::string path_and_mode = android::base::StringPrintf("%s,%d", path.c_str(), mode);
if (!SendRequest(ID_SEND, path_and_mode)) {
- Error("failed to send ID_SEND message '%s': %s", path_and_mode, strerror(errno));
+ Error("failed to send ID_SEND message '%s': %s", path_and_mode.c_str(),
+ strerror(errno));
return false;
}
struct stat st;
- if (stat(lpath, &st) == -1) {
- Error("cannot stat '%s': %s", lpath, strerror(errno));
+ if (stat(lpath.c_str(), &st) == -1) {
+ Error("cannot stat '%s': %s", lpath.c_str(), strerror(errno));
return false;
}
uint64_t total_size = st.st_size;
uint64_t bytes_copied = 0;
- unique_fd lfd(adb_open(lpath, O_RDONLY));
+ unique_fd lfd(adb_open(lpath.c_str(), O_RDONLY));
if (lfd < 0) {
- Error("opening '%s' locally failed: %s", lpath, strerror(errno));
+ Error("opening '%s' locally failed: %s", lpath.c_str(), strerror(errno));
return false;
}
@@ -487,7 +480,7 @@
while (true) {
int bytes_read = adb_read(lfd, sbuf.data, max - sizeof(SyncRequest));
if (bytes_read == -1) {
- Error("reading '%s' locally failed: %s", lpath, strerror(errno));
+ Error("reading '%s' locally failed: %s", lpath.c_str(), strerror(errno));
return false;
} else if (bytes_read == 0) {
break;
@@ -499,55 +492,53 @@
RecordBytesTransferred(bytes_read);
bytes_copied += bytes_read;
- // Check to see if we've received an error from the other side.
- if (ReceivedError(lpath, rpath)) {
- break;
- }
-
ReportProgress(rpath, bytes_copied, total_size);
}
syncmsg msg;
msg.data.id = ID_DONE;
msg.data.size = mtime;
- expect_done_ = true;
-
- // RecordFilesTransferred gets called in CopyDone.
+ RecordFileSent(lpath, rpath);
return WriteOrDie(lpath, rpath, &msg.data, sizeof(msg.data));
}
- bool CopyDone(const char* from, const char* to) {
+ bool ReadAcknowledgments() {
+ bool result = true;
+ while (!deferred_acknowledgements_.empty()) {
+ auto [from, to] = std::move(deferred_acknowledgements_.front());
+ deferred_acknowledgements_.pop_front();
+ result &= CopyDone(from, to);
+ }
+ return result;
+ }
+
+ bool CopyDone(const std::string& from, const std::string& to) {
syncmsg msg;
if (!ReadFdExactly(fd, &msg.status, sizeof(msg.status))) {
- Error("failed to copy '%s' to '%s': couldn't read from device", from, to);
+ Error("failed to copy '%s' to '%s': couldn't read from device", from.c_str(),
+ to.c_str());
return false;
}
if (msg.status.id == ID_OKAY) {
- if (expect_done_) {
- expect_done_ = false;
- RecordFilesTransferred(1);
- return true;
- } else {
- Error("failed to copy '%s' to '%s': received premature success", from, to);
- return true;
- }
+ return true;
}
if (msg.status.id != ID_FAIL) {
- Error("failed to copy '%s' to '%s': unknown reason %d", from, to, msg.status.id);
+ Error("failed to copy '%s' to '%s': unknown reason %d", from.c_str(), to.c_str(),
+ msg.status.id);
return false;
}
return ReportCopyFailure(from, to, msg);
}
- bool ReportCopyFailure(const char* from, const char* to, const syncmsg& msg) {
+ bool ReportCopyFailure(const std::string& from, const std::string& to, const syncmsg& msg) {
std::vector<char> buf(msg.status.msglen + 1);
if (!ReadFdExactly(fd, &buf[0], msg.status.msglen)) {
- Error("failed to copy '%s' to '%s'; failed to read reason (!): %s",
- from, to, strerror(errno));
+ Error("failed to copy '%s' to '%s'; failed to read reason (!): %s", from.c_str(),
+ to.c_str(), strerror(errno));
return false;
}
buf[msg.status.msglen] = 0;
- Error("failed to copy '%s' to '%s': remote %s", from, to, &buf[0]);
+ Error("failed to copy '%s' to '%s': remote %s", from.c_str(), to.c_str(), &buf[0]);
return false;
}
@@ -616,7 +607,7 @@
size_t max;
private:
- bool expect_done_;
+ std::deque<std::pair<std::string, std::string>> deferred_acknowledgements_;
FeatureSet features_;
bool have_stat_v2_;
bool have_ls_v2_;
@@ -629,16 +620,19 @@
return SendRequest(ID_QUIT, ""); // TODO: add a SendResponse?
}
- bool WriteOrDie(const char* from, const char* to, const void* data, size_t data_length) {
+ bool WriteOrDie(const std::string& from, const std::string& to, const void* data,
+ size_t data_length) {
if (!WriteFdExactly(fd, data, data_length)) {
if (errno == ECONNRESET) {
// Assume adbd told us why it was closing the connection, and
// try to read failure reason from adbd.
syncmsg msg;
if (!ReadFdExactly(fd, &msg.status, sizeof(msg.status))) {
- Error("failed to copy '%s' to '%s': no response: %s", from, to, strerror(errno));
+ Error("failed to copy '%s' to '%s': no response: %s", from.c_str(), to.c_str(),
+ strerror(errno));
} else if (msg.status.id != ID_FAIL) {
- Error("failed to copy '%s' to '%s': not ID_FAIL: %d", from, to, msg.status.id);
+ Error("failed to copy '%s' to '%s': not ID_FAIL: %d", from.c_str(), to.c_str(),
+ msg.status.id);
} else {
ReportCopyFailure(from, to, msg);
}
@@ -651,20 +645,20 @@
}
};
-static bool sync_ls(SyncConnection& sc, const char* path,
+static bool sync_ls(SyncConnection& sc, const std::string& path,
const std::function<sync_ls_cb>& func) {
return sc.SendLs(path) && sc.FinishLs(func);
}
-static bool sync_stat(SyncConnection& sc, const char* path, struct stat* st) {
+static bool sync_stat(SyncConnection& sc, const std::string& path, struct stat* st) {
return sc.SendStat(path) && sc.FinishStat(st);
}
-static bool sync_lstat(SyncConnection& sc, const char* path, struct stat* st) {
+static bool sync_lstat(SyncConnection& sc, const std::string& path, struct stat* st) {
return sc.SendLstat(path) && sc.FinishStat(st);
}
-static bool sync_stat_fallback(SyncConnection& sc, const char* path, struct stat* st) {
+static bool sync_stat_fallback(SyncConnection& sc, const std::string& path, struct stat* st) {
if (sync_stat(sc, path, st)) {
return true;
}
@@ -688,7 +682,7 @@
struct stat tmp_st;
st->st_mode &= ~S_IFMT;
- if (sync_lstat(sc, dir_path.c_str(), &tmp_st)) {
+ if (sync_lstat(sc, dir_path, &tmp_st)) {
st->st_mode |= S_IFDIR;
} else {
st->st_mode |= S_IFREG;
@@ -697,10 +691,8 @@
return true;
}
-static bool sync_send(SyncConnection& sc, const char* lpath, const char* rpath, unsigned mtime,
- mode_t mode, bool sync) {
- std::string path_and_mode = android::base::StringPrintf("%s,%d", rpath, mode);
-
+static bool sync_send(SyncConnection& sc, const std::string& lpath, const std::string& rpath,
+ unsigned mtime, mode_t mode, bool sync) {
if (sync) {
struct stat st;
if (sync_lstat(sc, rpath, &st)) {
@@ -714,41 +706,40 @@
if (S_ISLNK(mode)) {
#if !defined(_WIN32)
char buf[PATH_MAX];
- ssize_t data_length = readlink(lpath, buf, PATH_MAX - 1);
+ ssize_t data_length = readlink(lpath.c_str(), buf, PATH_MAX - 1);
if (data_length == -1) {
- sc.Error("readlink '%s' failed: %s", lpath, strerror(errno));
+ sc.Error("readlink '%s' failed: %s", lpath.c_str(), strerror(errno));
return false;
}
buf[data_length++] = '\0';
- if (!sc.SendSmallFile(path_and_mode.c_str(), lpath, rpath, mtime, buf, data_length)) {
+ if (!sc.SendSmallFile(rpath, mode, lpath, rpath, mtime, buf, data_length)) {
return false;
}
- return sc.CopyDone(lpath, rpath);
+ return true;
#endif
}
struct stat st;
- if (stat(lpath, &st) == -1) {
- sc.Error("failed to stat local file '%s': %s", lpath, strerror(errno));
+ if (stat(lpath.c_str(), &st) == -1) {
+ sc.Error("failed to stat local file '%s': %s", lpath.c_str(), strerror(errno));
return false;
}
if (st.st_size < SYNC_DATA_MAX) {
std::string data;
if (!android::base::ReadFileToString(lpath, &data, true)) {
- sc.Error("failed to read all of '%s': %s", lpath, strerror(errno));
+ sc.Error("failed to read all of '%s': %s", lpath.c_str(), strerror(errno));
return false;
}
- if (!sc.SendSmallFile(path_and_mode.c_str(), lpath, rpath, mtime,
- data.data(), data.size())) {
+ if (!sc.SendSmallFile(rpath, mode, lpath, rpath, mtime, data.data(), data.size())) {
return false;
}
} else {
- if (!sc.SendLargeFile(path_and_mode.c_str(), lpath, rpath, mtime)) {
+ if (!sc.SendLargeFile(rpath, mode, lpath, rpath, mtime)) {
return false;
}
}
- return sc.CopyDone(lpath, rpath);
+ return true;
}
static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath,
@@ -943,7 +934,7 @@
if (check_timestamps) {
for (const copyinfo& ci : file_list) {
- if (!sc.SendLstat(ci.rpath.c_str())) {
+ if (!sc.SendLstat(ci.rpath)) {
sc.Error("failed to send lstat");
return false;
}
@@ -965,7 +956,7 @@
if (list_only) {
sc.Println("would push: %s -> %s", ci.lpath.c_str(), ci.rpath.c_str());
} else {
- if (!sync_send(sc, ci.lpath.c_str(), ci.rpath.c_str(), ci.time, ci.mode, false)) {
+ if (!sync_send(sc, ci.lpath, ci.rpath, ci.time, ci.mode, false)) {
return false;
}
}
@@ -1069,6 +1060,7 @@
sc.ReportTransferRate(src_path, TransferDirection::push);
}
+ success &= sc.ReadAcknowledgments();
sc.ReportOverallTransferRate(TransferDirection::push);
return success;
}
diff --git a/base/liblog_symbols.cpp b/base/liblog_symbols.cpp
index d5dfcd2..8d59179 100644
--- a/base/liblog_symbols.cpp
+++ b/base/liblog_symbols.cpp
@@ -16,14 +16,20 @@
#include "liblog_symbols.h"
-#if defined(__ANDROID__) && !defined(NO_LIBLOG_DLSYM)
+#if defined(__ANDROID__)
+#if !defined(NO_LIBLOG_DLSYM) || defined(__ANDROID_APEX__)
+#define USE_DLSYM
+#endif
+#endif
+
+#ifdef USE_DLSYM
#include <dlfcn.h>
#endif
namespace android {
namespace base {
-#if defined(__ANDROID__) && !defined(NO_LIBLOG_DLSYM)
+#ifdef USE_DLSYM
const std::optional<LibLogFunctions>& GetLibLogFunctions() {
static std::optional<LibLogFunctions> liblog_functions = []() -> std::optional<LibLogFunctions> {
diff --git a/base/logging.cpp b/base/logging.cpp
index f42b996..9360a56 100644
--- a/base/logging.cpp
+++ b/base/logging.cpp
@@ -447,7 +447,7 @@
// See the comment in SetLogger().
static std::atomic<AbortFunction*> abort_function(nullptr);
auto* old_abort_function = abort_function.exchange(new AbortFunction(aborter));
- __android_log_set_aborter([](const char* abort_message) {
+ liblog_functions->__android_log_set_aborter([](const char* abort_message) {
auto& function = *abort_function.load(std::memory_order_acquire);
function(abort_message);
});
@@ -578,7 +578,7 @@
if (liblog_functions) {
__android_logger_data logger_data = {
sizeof(__android_logger_data), LOG_ID_DEFAULT, priority, tag, file, line};
- __android_log_write_logger_data(&logger_data, message);
+ liblog_functions->__android_log_write_logger_data(&logger_data, message);
} else {
if (tag == nullptr) {
std::lock_guard<std::recursive_mutex> lock(TagLock());
diff --git a/debuggerd/include/debuggerd/handler.h b/debuggerd/include/debuggerd/handler.h
index 4f24360..665d24a 100644
--- a/debuggerd/include/debuggerd/handler.h
+++ b/debuggerd/include/debuggerd/handler.h
@@ -19,7 +19,9 @@
#include <bionic/reserved_signals.h>
#include <signal.h>
#include <stdint.h>
+#include <string.h>
#include <sys/cdefs.h>
+#include <sys/system_properties.h>
#include <sys/types.h>
__BEGIN_DECLS
@@ -50,16 +52,21 @@
#define DEBUGGER_SIGNAL BIONIC_SIGNAL_DEBUGGER
static void __attribute__((__unused__)) debuggerd_register_handlers(struct sigaction* action) {
- sigaction(SIGABRT, action, nullptr);
- sigaction(SIGBUS, action, nullptr);
- sigaction(SIGFPE, action, nullptr);
- sigaction(SIGILL, action, nullptr);
- sigaction(SIGSEGV, action, nullptr);
-#if defined(SIGSTKFLT)
- sigaction(SIGSTKFLT, action, nullptr);
-#endif
- sigaction(SIGSYS, action, nullptr);
- sigaction(SIGTRAP, action, nullptr);
+ char value[PROP_VALUE_MAX] = "";
+ bool enabled =
+ !(__system_property_get("ro.debuggable", value) > 0 && !strcmp(value, "1") &&
+ __system_property_get("debug.debuggerd.disable", value) > 0 && !strcmp(value, "1"));
+ if (enabled) {
+ sigaction(SIGABRT, action, nullptr);
+ sigaction(SIGBUS, action, nullptr);
+ sigaction(SIGFPE, action, nullptr);
+ sigaction(SIGILL, action, nullptr);
+ sigaction(SIGSEGV, action, nullptr);
+ sigaction(SIGSTKFLT, action, nullptr);
+ sigaction(SIGSYS, action, nullptr);
+ sigaction(SIGTRAP, action, nullptr);
+ }
+
sigaction(BIONIC_SIGNAL_DEBUGGER, action, nullptr);
}
diff --git a/fs_mgr/TEST_MAPPING b/fs_mgr/TEST_MAPPING
index 705d4e3..676f446 100644
--- a/fs_mgr/TEST_MAPPING
+++ b/fs_mgr/TEST_MAPPING
@@ -13,7 +13,7 @@
"name": "fiemap_writer_test"
},
{
- "name": "vts_libsnapshot_test_presubmit"
+ "name": "vts_libsnapshot_test"
}
]
}
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 59cae61..d509e71 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -96,6 +96,7 @@
using android::base::Basename;
using android::base::GetBoolProperty;
+using android::base::Readlink;
using android::base::Realpath;
using android::base::SetProperty;
using android::base::StartsWith;
@@ -1588,6 +1589,61 @@
}
}
+static bool UnwindDmDeviceStack(const std::string& block_device,
+ std::vector<std::string>* dm_stack) {
+ if (!StartsWith(block_device, "/dev/block/")) {
+ LWARNING << block_device << " is not a block device";
+ return false;
+ }
+ std::string current = block_device;
+ DeviceMapper& dm = DeviceMapper::Instance();
+ while (true) {
+ dm_stack->push_back(current);
+ if (!dm.IsDmBlockDevice(current)) {
+ break;
+ }
+ auto parent = dm.GetParentBlockDeviceByPath(current);
+ if (!parent) {
+ return false;
+ }
+ current = *parent;
+ }
+ return true;
+}
+
+FstabEntry* fs_mgr_get_mounted_entry_for_userdata(Fstab* fstab, const FstabEntry& mounted_entry) {
+ if (mounted_entry.mount_point != "/data") {
+ LERROR << mounted_entry.mount_point << " is not /data";
+ return nullptr;
+ }
+ std::vector<std::string> dm_stack;
+ if (!UnwindDmDeviceStack(mounted_entry.blk_device, &dm_stack)) {
+ LERROR << "Failed to unwind dm-device stack for " << mounted_entry.blk_device;
+ return nullptr;
+ }
+ for (auto& entry : *fstab) {
+ if (entry.mount_point != "/data") {
+ continue;
+ }
+ std::string block_device;
+ if (entry.fs_mgr_flags.logical) {
+ if (!fs_mgr_update_logical_partition(&entry)) {
+ LERROR << "Failed to update logic partition " << entry.blk_device;
+ continue;
+ }
+ block_device = entry.blk_device;
+ } else if (!Readlink(entry.blk_device, &block_device)) {
+ PWARNING << "Failed to read link " << entry.blk_device;
+ block_device = entry.blk_device;
+ }
+ if (std::find(dm_stack.begin(), dm_stack.end(), block_device) != dm_stack.end()) {
+ return &entry;
+ }
+ }
+ LERROR << "Didn't find entry that was used to mount /data onto " << mounted_entry.blk_device;
+ return nullptr;
+}
+
// TODO(b/143970043): return different error codes based on which step failed.
int fs_mgr_remount_userdata_into_checkpointing(Fstab* fstab) {
Fstab proc_mounts;
@@ -1596,16 +1652,13 @@
return -1;
}
std::string block_device;
- if (auto entry = GetEntryForMountPoint(&proc_mounts, "/data"); entry != nullptr) {
- // Note: we don't care about a userdata wrapper here, since it's safe
- // to remount on top of the bow device instead, there will be no
- // conflicts.
- block_device = entry->blk_device;
- } else {
+ auto mounted_entry = GetEntryForMountPoint(&proc_mounts, "/data");
+ if (mounted_entry == nullptr) {
LERROR << "/data is not mounted";
return -1;
}
- auto fstab_entry = GetMountedEntryForUserdata(fstab);
+ block_device = mounted_entry->blk_device;
+ auto fstab_entry = fs_mgr_get_mounted_entry_for_userdata(fstab, *mounted_entry);
if (fstab_entry == nullptr) {
LERROR << "Can't find /data in fstab";
return -1;
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index a36934a..f3f1cb7 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -814,89 +814,6 @@
return entries;
}
-static std::string ResolveBlockDevice(const std::string& block_device) {
- if (!StartsWith(block_device, "/dev/block/")) {
- LWARNING << block_device << " is not a block device";
- return block_device;
- }
- std::string name = block_device.substr(5);
- if (!StartsWith(name, "block/dm-")) {
- // Not a dm-device, but might be a symlink. Optimistically try to readlink.
- std::string result;
- if (Readlink(block_device, &result)) {
- return result;
- } else if (errno == EINVAL) {
- // After all, it wasn't a symlink.
- return block_device;
- } else {
- LERROR << "Failed to readlink " << block_device;
- return "";
- }
- }
- // It's a dm-device, let's find what's inside!
- std::string sys_dir = "/sys/" + name;
- while (true) {
- std::string slaves_dir = sys_dir + "/slaves";
- std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(slaves_dir.c_str()), closedir);
- if (!dir) {
- LERROR << "Failed to open " << slaves_dir;
- return "";
- }
- std::string sub_device_name = "";
- for (auto entry = readdir(dir.get()); entry; entry = readdir(dir.get())) {
- if (entry->d_type != DT_LNK) continue;
- if (!sub_device_name.empty()) {
- LERROR << "Too many slaves in " << slaves_dir;
- return "";
- }
- sub_device_name = entry->d_name;
- }
- if (sub_device_name.empty()) {
- LERROR << "No slaves in " << slaves_dir;
- return "";
- }
- if (!StartsWith(sub_device_name, "dm-")) {
- // Not a dm-device! We can stop now.
- return "/dev/block/" + sub_device_name;
- }
- // Still a dm-device, keep digging.
- sys_dir = "/sys/block/" + sub_device_name;
- }
-}
-
-FstabEntry* GetMountedEntryForUserdata(Fstab* fstab) {
- Fstab mounts;
- if (!ReadFstabFromFile("/proc/mounts", &mounts)) {
- LERROR << "Failed to read /proc/mounts";
- return nullptr;
- }
- auto mounted_entry = GetEntryForMountPoint(&mounts, "/data");
- if (mounted_entry == nullptr) {
- LWARNING << "/data is not mounted";
- return nullptr;
- }
- std::string resolved_block_device = ResolveBlockDevice(mounted_entry->blk_device);
- if (resolved_block_device.empty()) {
- return nullptr;
- }
- LINFO << "/data is mounted on " << resolved_block_device;
- for (auto& entry : *fstab) {
- if (entry.mount_point != "/data") {
- continue;
- }
- std::string block_device;
- if (!Readlink(entry.blk_device, &block_device)) {
- LWARNING << "Failed to readlink " << entry.blk_device;
- block_device = entry.blk_device;
- }
- if (block_device == resolved_block_device) {
- return &entry;
- }
- }
- LERROR << "Didn't find entry that was used to mount /data";
- return nullptr;
-}
-
std::set<std::string> GetBootDevices() {
// First check the kernel commandline, then try the device tree otherwise
std::string dt_file_name = get_android_dt_dir() + "/boot_devices";
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 9bc38f9..3d556c9 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -107,6 +107,10 @@
// it destroys verity devices from device mapper after the device is unmounted.
int fs_mgr_umount_all(android::fs_mgr::Fstab* fstab);
+// Finds a entry in |fstab| that was used to mount a /data |mounted_entry| from
+// /proc/mounts.
+android::fs_mgr::FstabEntry* fs_mgr_get_mounted_entry_for_userdata(
+ android::fs_mgr::Fstab* fstab, const android::fs_mgr::FstabEntry& mounted_entry);
int fs_mgr_remount_userdata_into_checkpointing(android::fs_mgr::Fstab* fstab);
// Finds the dm_bow device on which this block device is stacked, or returns
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index c94d7ac..009c04c 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -102,7 +102,6 @@
FstabEntry* GetEntryForMountPoint(Fstab* fstab, const std::string& path);
// The Fstab can contain multiple entries for the same mount point with different configurations.
std::vector<FstabEntry*> GetEntriesForMountPoint(Fstab* fstab, const std::string& path);
-FstabEntry* GetMountedEntryForUserdata(Fstab* fstab);
// This method builds DSU fstab entries and transfer the fstab.
//
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index 254fbed..673e145 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -120,7 +120,7 @@
return false;
}
if (!WaitForFileDeleted(unique_path, timeout_ms)) {
- LOG(ERROR) << "Timeout out waiting for " << unique_path << " to be deleted";
+ LOG(ERROR) << "Failed waiting for " << unique_path << " to be deleted";
return false;
}
return true;
@@ -161,7 +161,7 @@
return true;
}
if (!WaitForFile(unique_path, timeout_ms)) {
- LOG(ERROR) << "Timed out waiting for device path: " << unique_path;
+ LOG(ERROR) << "Failed waiting for device path: " << unique_path;
DeleteDevice(name);
return false;
}
diff --git a/fs_mgr/libdm/utility.cpp b/fs_mgr/libdm/utility.cpp
index f252565..0eb59ab 100644
--- a/fs_mgr/libdm/utility.cpp
+++ b/fs_mgr/libdm/utility.cpp
@@ -19,6 +19,8 @@
#include <thread>
+#include <android-base/logging.h>
+
using namespace std::literals;
namespace android {
@@ -45,7 +47,11 @@
// If the file exists but returns EPERM or something, we consider the
// condition met.
if (access(path.c_str(), F_OK) != 0) {
- if (errno == ENOENT) return WaitResult::Wait;
+ if (errno == ENOENT) {
+ return WaitResult::Wait;
+ }
+ PLOG(ERROR) << "access failed: " << path;
+ return WaitResult::Fail;
}
return WaitResult::Done;
};
@@ -54,9 +60,13 @@
bool WaitForFileDeleted(const std::string& path, const std::chrono::milliseconds& timeout_ms) {
auto condition = [&]() -> WaitResult {
- if (access(path.c_str(), F_OK) == 0 || errno != ENOENT) {
+ if (access(path.c_str(), F_OK) == 0) {
return WaitResult::Wait;
}
+ if (errno != ENOENT) {
+ PLOG(ERROR) << "access failed: " << path;
+ return WaitResult::Fail;
+ }
return WaitResult::Done;
};
return WaitForCondition(condition, timeout_ms);
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 2d62617..d670ca0 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -96,16 +96,6 @@
static_libs: [
"libfs_mgr_binder"
],
-
- shared_libs: [
- // TODO(b/148818798): remove when parent bug is fixed
- "libutilscallstack",
- ],
- cflags: [
- "-g",
- "-O0",
- "-DLIBSNAPSHOT_USE_CALLSTACK",
- ],
}
cc_library_static {
@@ -179,9 +169,6 @@
"libsparse",
"libutils",
"libz",
-
- // TODO(b/148818798): remove when parent bug is fixed
- "libutilscallstack",
],
static_libs: [
"libfs_mgr",
@@ -208,14 +195,6 @@
defaults: ["libsnapshot_test_defaults"],
}
-cc_test {
- name: "vts_libsnapshot_test_presubmit",
- defaults: ["libsnapshot_test_defaults"],
- cppflags: [
- "-DSKIP_TEST_IN_PRESUBMIT",
- ],
-}
-
cc_binary {
name: "snapshotctl",
srcs: [
@@ -239,11 +218,5 @@
"libprotobuf-cpp-lite",
"libstatslog",
"libutils",
-
- // TODO(b/148818798): remove when parent bug is fixed.
- "libutilscallstack",
- ],
- init_rc: [
- "snapshotctl.rc",
],
}
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/return.h b/fs_mgr/libsnapshot/include/libsnapshot/return.h
index 1f132fa..dedc445 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/return.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/return.h
@@ -30,7 +30,6 @@
enum class ErrorCode : int32_t {
SUCCESS = static_cast<int32_t>(FiemapStatus::ErrorCode::SUCCESS),
ERROR = static_cast<int32_t>(FiemapStatus::ErrorCode::ERROR),
- NEEDS_REBOOT = ERROR + 1,
NO_SPACE = static_cast<int32_t>(FiemapStatus::ErrorCode::NO_SPACE),
};
ErrorCode error_code() const { return error_code_; }
@@ -43,7 +42,6 @@
static Return Ok() { return Return(ErrorCode::SUCCESS); }
static Return Error() { return Return(ErrorCode::ERROR); }
static Return NoSpace(uint64_t size) { return Return(ErrorCode::NO_SPACE, size); }
- static Return NeedsReboot() { return Return(ErrorCode::NEEDS_REBOOT); }
// Does not set required_size_ properly even when status.error_code() == NO_SPACE.
explicit Return(const FiemapStatus& status)
: error_code_(FromFiemapStatusErrorCode(status.error_code())), required_size_(0) {}
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index b440c71..957c26c 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -74,7 +74,8 @@
static constexpr const std::string_view kCowGroupName = "cow";
-bool SourceCopyOperationIsClone(const chromeos_update_engine::InstallOperation& operation);
+bool OptimizeSourceCopyOperation(const chromeos_update_engine::InstallOperation& operation,
+ chromeos_update_engine::InstallOperation* optimized);
enum class CreateResult : unsigned int {
ERROR,
@@ -125,6 +126,9 @@
// might be needed to perform first-stage mounts.
static bool IsSnapshotManagerNeeded();
+ // Helper function for second stage init to restorecon on the rollback indicator.
+ static std::string GetGlobalRollbackIndicatorPath();
+
// Begin an update. This must be called before creating any snapshots. It
// will fail if GetUpdateState() != None.
bool BeginUpdate();
@@ -140,7 +144,6 @@
// Before calling this function, all snapshots must be mapped.
bool FinishedSnapshotWrites();
- private:
// Initiate a merge on all snapshot devices. This should only be used after an
// update has been marked successful after booting.
bool InitiateMerge();
@@ -149,7 +152,11 @@
// /data is mounted.
//
// If a merge is in progress, this function will block until the merge is
- // completed. If a merge or update was cancelled, this will clean up any
+ // completed.
+ // - Callback is called periodically during the merge. If callback()
+ // returns false during the merge, ProcessUpdateState() will pause
+ // and returns Merging.
+ // If a merge or update was cancelled, this will clean up any
// update artifacts and return.
//
// Note that after calling this, GetUpdateState() may still return that a
@@ -169,26 +176,8 @@
//
// The optional callback allows the caller to periodically check the
// progress with GetUpdateState().
- UpdateState ProcessUpdateState(const std::function<void()>& callback = {});
-
- public:
- // Initiate the merge if necessary, then wait for the merge to finish.
- // See InitiateMerge() and ProcessUpdateState() for details.
- // Returns:
- // - None if no merge to initiate
- // - Unverified if called on the source slot
- // - MergeCompleted if merge is completed
- // - other states indicating an error has occurred
- UpdateState InitiateMergeAndWait(SnapshotMergeReport* report = nullptr);
-
- // Wait for the merge if rebooted into the new slot. Does NOT initiate a
- // merge. If the merge has not been initiated (but should be), wait.
- // Returns:
- // - Return::Ok(): there is no merge or merge finishes
- // - Return::NeedsReboot(): merge finishes but need a reboot before
- // applying the next update.
- // - Return::Error(): other irrecoverable errors
- Return WaitForMerge();
+ UpdateState ProcessUpdateState(const std::function<bool()>& callback = {},
+ const std::function<bool()>& before_cancel = {});
// Find the status of the current update, if any.
//
@@ -375,14 +364,23 @@
// Check for a cancelled or rolled back merge, returning true if such a
// condition was detected and handled.
- bool HandleCancelledUpdate(LockedFile* lock);
+ bool HandleCancelledUpdate(LockedFile* lock, const std::function<bool()>& before_cancel);
// Helper for HandleCancelledUpdate. Assumes booting from new slot.
bool AreAllSnapshotsCancelled(LockedFile* lock);
+ // Determine whether partition names in |snapshots| have been flashed and
+ // store result to |out|.
+ // Return true if values are successfully retrieved and false on error
+ // (e.g. super partition metadata cannot be read). When it returns true,
+ // |out| stores true for partitions that have been flashed and false for
+ // partitions that have not been flashed.
+ bool GetSnapshotFlashingStatus(LockedFile* lock, const std::vector<std::string>& snapshots,
+ std::map<std::string, bool>* out);
+
// Remove artifacts created by the update process, such as snapshots, and
// set the update state to None.
- bool RemoveAllUpdateState(LockedFile* lock);
+ bool RemoveAllUpdateState(LockedFile* lock, const std::function<bool()>& prolog = {});
// Interact with /metadata/ota.
std::unique_ptr<LockedFile> OpenLock(int lock_flags);
@@ -437,8 +435,8 @@
// UpdateState::MergeCompleted
// UpdateState::MergeFailed
// UpdateState::MergeNeedsReboot
- UpdateState CheckMergeState();
- UpdateState CheckMergeState(LockedFile* lock);
+ UpdateState CheckMergeState(const std::function<bool()>& before_cancel);
+ UpdateState CheckMergeState(LockedFile* lock, const std::function<bool()>& before_cancel);
UpdateState CheckTargetMergeState(LockedFile* lock, const std::string& name);
// Interact with status files under /metadata/ota/snapshots.
@@ -513,6 +511,11 @@
std::string ReadUpdateSourceSlotSuffix();
+ // Helper for RemoveAllSnapshots.
+ // Check whether |name| should be deleted as a snapshot name.
+ bool ShouldDeleteSnapshot(LockedFile* lock, const std::map<std::string, bool>& flashing_status,
+ Slot current_slot, const std::string& name);
+
std::string gsid_dir_;
std::string metadata_dir_;
std::unique_ptr<IDeviceInfo> device_;
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h
new file mode 100644
index 0000000..91dd34f
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h
@@ -0,0 +1,59 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <chrono>
+#include <memory>
+
+#include <android/snapshot/snapshot.pb.h>
+#include <libsnapshot/snapshot.h>
+
+namespace android {
+namespace snapshot {
+
+class SnapshotMergeStats {
+ public:
+ // Not thread safe.
+ static SnapshotMergeStats* GetInstance(SnapshotManager& manager);
+
+ // Called when merge starts or resumes.
+ bool Start();
+ void set_state(android::snapshot::UpdateState state);
+
+ // Called when merge ends. Properly clean up permanent storage.
+ class Result {
+ public:
+ virtual ~Result() {}
+ virtual const SnapshotMergeReport& report() const = 0;
+ // Time between successful Start() / Resume() to Finish().
+ virtual std::chrono::steady_clock::duration merge_time() const = 0;
+ };
+ std::unique_ptr<Result> Finish();
+
+ private:
+ bool ReadState();
+ bool WriteState();
+ bool DeleteState();
+ SnapshotMergeStats(const std::string& path);
+
+ std::string path_;
+ SnapshotMergeReport report_;
+ // Time of the last successful Start() / Resume() call.
+ std::chrono::time_point<std::chrono::steady_clock> start_time_;
+ bool running_{false};
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/partition_cow_creator.cpp b/fs_mgr/libsnapshot/partition_cow_creator.cpp
index 61f5c0c..efdb59f 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator.cpp
@@ -62,17 +62,68 @@
return false;
}
-bool SourceCopyOperationIsClone(const InstallOperation& operation) {
- using ChromeOSExtent = chromeos_update_engine::Extent;
- if (operation.src_extents().size() != operation.dst_extents().size()) {
+bool OptimizeSourceCopyOperation(const InstallOperation& operation, InstallOperation* optimized) {
+ if (operation.type() != InstallOperation::SOURCE_COPY) {
return false;
}
- return std::equal(operation.src_extents().begin(), operation.src_extents().end(),
- operation.dst_extents().begin(),
- [](const ChromeOSExtent& src, const ChromeOSExtent& dst) {
- return src.start_block() == dst.start_block() &&
- src.num_blocks() == dst.num_blocks();
- });
+
+ optimized->Clear();
+ optimized->set_type(InstallOperation::SOURCE_COPY);
+
+ const auto& src_extents = operation.src_extents();
+ const auto& dst_extents = operation.dst_extents();
+
+ // If input is empty, skip by returning an empty result.
+ if (src_extents.empty() && dst_extents.empty()) {
+ return true;
+ }
+
+ auto s_it = src_extents.begin();
+ auto d_it = dst_extents.begin();
+ uint64_t s_offset = 0; // offset within *s_it
+ uint64_t d_offset = 0; // offset within *d_it
+ bool is_optimized = false;
+
+ while (s_it != src_extents.end() || d_it != dst_extents.end()) {
+ if (s_it == src_extents.end() || d_it == dst_extents.end()) {
+ LOG(ERROR) << "number of blocks do not equal in src_extents and dst_extents";
+ return false;
+ }
+ if (s_it->num_blocks() <= s_offset || d_it->num_blocks() <= d_offset) {
+ LOG(ERROR) << "Offset goes out of bounds.";
+ return false;
+ }
+
+ // Check the next |step| blocks, where |step| is the min of remaining blocks in the current
+ // source extent and current destination extent.
+ auto s_step = s_it->num_blocks() - s_offset;
+ auto d_step = d_it->num_blocks() - d_offset;
+ auto step = std::min(s_step, d_step);
+
+ bool moved = s_it->start_block() + s_offset != d_it->start_block() + d_offset;
+ if (moved) {
+ // If the next |step| blocks are not copied to the same location, add them to result.
+ AppendExtent(optimized->mutable_src_extents(), s_it->start_block() + s_offset, step);
+ AppendExtent(optimized->mutable_dst_extents(), d_it->start_block() + d_offset, step);
+ } else {
+ // The next |step| blocks are optimized out.
+ is_optimized = true;
+ }
+
+ // Advance offsets by |step|, and go to the next non-empty extent if the current extent is
+ // depleted.
+ s_offset += step;
+ d_offset += step;
+ while (s_it != src_extents.end() && s_offset >= s_it->num_blocks()) {
+ ++s_it;
+ s_offset = 0;
+ }
+ while (d_it != dst_extents.end() && d_offset >= d_it->num_blocks()) {
+ ++d_it;
+ d_offset = 0;
+ }
+ }
+ return is_optimized;
}
void WriteExtent(DmSnapCowSizeCalculator* sc, const chromeos_update_engine::Extent& de,
@@ -101,12 +152,15 @@
if (operations == nullptr) return sc.cow_size_bytes();
for (const auto& iop : *operations) {
- // Do not allocate space for operations that are going to be skipped
+ const InstallOperation* written_op = &iop;
+ InstallOperation buf;
+ // Do not allocate space for extents that are going to be skipped
// during OTA application.
- if (iop.type() == InstallOperation::SOURCE_COPY && SourceCopyOperationIsClone(iop))
- continue;
+ if (iop.type() == InstallOperation::SOURCE_COPY && OptimizeSourceCopyOperation(iop, &buf)) {
+ written_op = &buf;
+ }
- for (const auto& de : iop.dst_extents()) {
+ for (const auto& de : written_op->dst_extents()) {
WriteExtent(&sc, de, sectors_per_block);
}
}
diff --git a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
index 9da3f05..526f874 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
@@ -12,6 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <optional>
+#include <tuple>
+
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <libdm/dm.h>
@@ -26,6 +29,13 @@
using namespace android::fs_mgr;
+using chromeos_update_engine::InstallOperation;
+using UeExtent = chromeos_update_engine::Extent;
+using google::protobuf::RepeatedPtrField;
+using ::testing::Matches;
+using ::testing::Pointwise;
+using ::testing::Truly;
+
namespace android {
namespace snapshot {
@@ -213,5 +223,76 @@
}
}
+void BlocksToExtents(const std::vector<uint64_t>& blocks,
+ google::protobuf::RepeatedPtrField<UeExtent>* extents) {
+ for (uint64_t block : blocks) {
+ AppendExtent(extents, block, 1);
+ }
+}
+
+template <typename T>
+std::vector<uint64_t> ExtentsToBlocks(const T& extents) {
+ std::vector<uint64_t> blocks;
+ for (const auto& extent : extents) {
+ for (uint64_t offset = 0; offset < extent.num_blocks(); ++offset) {
+ blocks.push_back(extent.start_block() + offset);
+ }
+ }
+ return blocks;
+}
+
+InstallOperation CreateCopyOp(const std::vector<uint64_t>& src_blocks,
+ const std::vector<uint64_t>& dst_blocks) {
+ InstallOperation op;
+ op.set_type(InstallOperation::SOURCE_COPY);
+ BlocksToExtents(src_blocks, op.mutable_src_extents());
+ BlocksToExtents(dst_blocks, op.mutable_dst_extents());
+ return op;
+}
+
+// ExtentEqual(tuple<UeExtent, UeExtent>)
+MATCHER(ExtentEqual, "") {
+ auto&& [a, b] = arg;
+ return a.start_block() == b.start_block() && a.num_blocks() == b.num_blocks();
+}
+
+struct OptimizeOperationTestParam {
+ InstallOperation input;
+ std::optional<InstallOperation> expected_output;
+};
+
+class OptimizeOperationTest : public ::testing::TestWithParam<OptimizeOperationTestParam> {};
+TEST_P(OptimizeOperationTest, Test) {
+ InstallOperation actual_output;
+ EXPECT_EQ(GetParam().expected_output.has_value(),
+ OptimizeSourceCopyOperation(GetParam().input, &actual_output))
+ << "OptimizeSourceCopyOperation should "
+ << (GetParam().expected_output.has_value() ? "succeed" : "fail");
+ if (!GetParam().expected_output.has_value()) return;
+ EXPECT_THAT(actual_output.src_extents(),
+ Pointwise(ExtentEqual(), GetParam().expected_output->src_extents()));
+ EXPECT_THAT(actual_output.dst_extents(),
+ Pointwise(ExtentEqual(), GetParam().expected_output->dst_extents()));
+}
+
+std::vector<OptimizeOperationTestParam> GetOptimizeOperationTestParams() {
+ return {
+ {CreateCopyOp({}, {}), CreateCopyOp({}, {})},
+ {CreateCopyOp({1, 2, 4}, {1, 2, 4}), CreateCopyOp({}, {})},
+ {CreateCopyOp({1, 2, 3}, {4, 5, 6}), std::nullopt},
+ {CreateCopyOp({3, 2}, {1, 2}), CreateCopyOp({3}, {1})},
+ {CreateCopyOp({5, 6, 3, 4, 1, 2}, {1, 2, 3, 4, 5, 6}),
+ CreateCopyOp({5, 6, 1, 2}, {1, 2, 5, 6})},
+ {CreateCopyOp({1, 2, 3, 5, 5, 6}, {5, 6, 3, 4, 1, 2}),
+ CreateCopyOp({1, 2, 5, 5, 6}, {5, 6, 4, 1, 2})},
+ {CreateCopyOp({1, 2, 5, 6, 9, 10}, {1, 4, 5, 6, 7, 8}),
+ CreateCopyOp({2, 9, 10}, {4, 7, 8})},
+ {CreateCopyOp({2, 3, 3, 4, 4}, {1, 2, 3, 4, 5}), CreateCopyOp({2, 3, 4}, {1, 2, 5})},
+ };
+}
+
+INSTANTIATE_TEST_CASE_P(Snapshot, OptimizeOperationTest,
+ ::testing::ValuesIn(GetOptimizeOperationTestParams()));
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/return.cpp b/fs_mgr/libsnapshot/return.cpp
index 6559c12..cc64af5 100644
--- a/fs_mgr/libsnapshot/return.cpp
+++ b/fs_mgr/libsnapshot/return.cpp
@@ -24,8 +24,6 @@
switch (error_code()) {
case ErrorCode::ERROR:
return "Error";
- case ErrorCode::NEEDS_REBOOT:
- return "Retry after reboot";
case ErrorCode::SUCCESS:
[[fallthrough]];
case ErrorCode::NO_SPACE:
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 2fe06fb..7e84c48 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -21,7 +21,6 @@
#include <sys/unistd.h>
#include <optional>
-#include <sstream>
#include <thread>
#include <unordered_set>
@@ -38,15 +37,11 @@
#include <libfiemap/image_manager.h>
#include <liblp/liblp.h>
-#ifdef LIBSNAPSHOT_USE_CALLSTACK
-#include <utils/CallStack.h>
-#endif
-
#include <android/snapshot/snapshot.pb.h>
+#include <libsnapshot/snapshot_stats.h>
#include "device_info.h"
#include "partition_cow_creator.h"
#include "snapshot_metadata_updater.h"
-#include "snapshot_stats.h"
#include "utility.h"
namespace android {
@@ -81,6 +76,7 @@
using namespace std::string_literals;
static constexpr char kBootIndicatorPath[] = "/metadata/ota/snapshot-boot";
+static constexpr char kRollbackIndicatorPath[] = "/metadata/ota/rollback-indicator";
static constexpr auto kUpdateStateCheckInterval = 2s;
// Note: IImageManager is an incomplete type in the header, so the default
@@ -219,27 +215,13 @@
return true;
}
-bool SnapshotManager::RemoveAllUpdateState(LockedFile* lock) {
- LOG(INFO) << "Removing all update state.";
-
-#ifdef LIBSNAPSHOT_USE_CALLSTACK
- LOG(WARNING) << "Logging stack; see b/148818798.";
- // Do not use CallStack's log functions because snapshotctl relies on
- // android-base/logging to save log to files.
- // TODO(b/148818798): remove this before we ship.
- CallStack callstack;
- callstack.update();
- auto callstack_str = callstack.toString();
- LOG(WARNING) << callstack_str.c_str();
- std::stringstream path;
- path << "/data/misc/snapshotctl_log/libsnapshot." << Now() << ".log";
- std::string path_str = path.str();
- android::base::WriteStringToFile(callstack_str.c_str(), path_str);
- if (chmod(path_str.c_str(), 0644) == -1) {
- PLOG(WARNING) << "Unable to chmod 0644 "
- << ", file maybe dropped from bugreport:" << path_str;
+bool SnapshotManager::RemoveAllUpdateState(LockedFile* lock, const std::function<bool()>& prolog) {
+ if (prolog && !prolog()) {
+ LOG(WARNING) << "Can't RemoveAllUpdateState: prolog failed.";
+ return false;
}
-#endif
+
+ LOG(INFO) << "Removing all update state.";
if (!RemoveAllSnapshots(lock)) {
LOG(ERROR) << "Could not remove all snapshots";
@@ -789,9 +771,10 @@
// Note that when a merge fails, we will *always* try again to complete the
// merge each time the device boots. There is no harm in doing so, and if
// the problem was transient, we might manage to get a new outcome.
-UpdateState SnapshotManager::ProcessUpdateState(const std::function<void()>& callback) {
+UpdateState SnapshotManager::ProcessUpdateState(const std::function<bool()>& callback,
+ const std::function<bool()>& before_cancel) {
while (true) {
- UpdateState state = CheckMergeState();
+ UpdateState state = CheckMergeState(before_cancel);
if (state == UpdateState::MergeFailed) {
AcknowledgeMergeFailure();
}
@@ -801,8 +784,8 @@
return state;
}
- if (callback) {
- callback();
+ if (callback && !callback()) {
+ return state;
}
// This wait is not super time sensitive, so we have a relatively
@@ -811,24 +794,27 @@
}
}
-UpdateState SnapshotManager::CheckMergeState() {
+UpdateState SnapshotManager::CheckMergeState(const std::function<bool()>& before_cancel) {
auto lock = LockExclusive();
if (!lock) {
return UpdateState::MergeFailed;
}
- UpdateState state = CheckMergeState(lock.get());
+ UpdateState state = CheckMergeState(lock.get(), before_cancel);
if (state == UpdateState::MergeCompleted) {
// Do this inside the same lock. Failures get acknowledged without the
// lock, because flock() might have failed.
AcknowledgeMergeSuccess(lock.get());
} else if (state == UpdateState::Cancelled) {
- RemoveAllUpdateState(lock.get());
+ if (!RemoveAllUpdateState(lock.get(), before_cancel)) {
+ return ReadSnapshotUpdateStatus(lock.get()).state();
+ }
}
return state;
}
-UpdateState SnapshotManager::CheckMergeState(LockedFile* lock) {
+UpdateState SnapshotManager::CheckMergeState(LockedFile* lock,
+ const std::function<bool()>& before_cancel) {
UpdateState state = ReadUpdateState(lock);
switch (state) {
case UpdateState::None:
@@ -849,7 +835,7 @@
// This is an edge case. Normally cancelled updates are detected
// via the merge poll below, but if we never started a merge, we
// need to also check here.
- if (HandleCancelledUpdate(lock)) {
+ if (HandleCancelledUpdate(lock, before_cancel)) {
return UpdateState::Cancelled;
}
return state;
@@ -1003,7 +989,7 @@
}
std::string SnapshotManager::GetRollbackIndicatorPath() {
- return metadata_dir_ + "/rollback-indicator";
+ return metadata_dir_ + "/" + android::base::Basename(kRollbackIndicatorPath);
}
void SnapshotManager::AcknowledgeMergeSuccess(LockedFile* lock) {
@@ -1169,7 +1155,8 @@
return true;
}
-bool SnapshotManager::HandleCancelledUpdate(LockedFile* lock) {
+bool SnapshotManager::HandleCancelledUpdate(LockedFile* lock,
+ const std::function<bool()>& before_cancel) {
auto slot = GetCurrentSlot();
if (slot == Slot::Unknown) {
return false;
@@ -1177,15 +1164,30 @@
// If all snapshots were reflashed, then cancel the entire update.
if (AreAllSnapshotsCancelled(lock)) {
- RemoveAllUpdateState(lock);
- return true;
+ LOG(WARNING) << "Detected re-flashing, cancelling unverified update.";
+ return RemoveAllUpdateState(lock, before_cancel);
}
- // This unverified update might be rolled back, or it might not (b/147347110
- // comment #77). Take no action, as update_engine is responsible for deciding
- // whether to cancel.
- LOG(ERROR) << "Update state is being processed before reboot, taking no action.";
- return false;
+ // If update has been rolled back, then cancel the entire update.
+ // Client (update_engine) is responsible for doing additional cleanup work on its own states
+ // when ProcessUpdateState() returns UpdateState::Cancelled.
+ auto current_slot = GetCurrentSlot();
+ if (current_slot != Slot::Source) {
+ LOG(INFO) << "Update state is being processed while booting at " << current_slot
+ << " slot, taking no action.";
+ return false;
+ }
+
+ // current_slot == Source. Attempt to detect rollbacks.
+ if (access(GetRollbackIndicatorPath().c_str(), F_OK) != 0) {
+ // This unverified update is not attempted. Take no action.
+ PLOG(INFO) << "Rollback indicator not detected. "
+ << "Update state is being processed before reboot, taking no action.";
+ return false;
+ }
+
+ LOG(WARNING) << "Detected rollback, cancelling unverified update.";
+ return RemoveAllUpdateState(lock, before_cancel);
}
std::unique_ptr<LpMetadata> SnapshotManager::ReadCurrentMetadata() {
@@ -1219,6 +1221,28 @@
return true;
}
+ std::map<std::string, bool> flashing_status;
+
+ if (!GetSnapshotFlashingStatus(lock, snapshots, &flashing_status)) {
+ LOG(WARNING) << "Failed to determine whether partitions have been flashed. Not"
+ << "removing update states.";
+ return false;
+ }
+
+ bool all_snapshots_cancelled = std::all_of(flashing_status.begin(), flashing_status.end(),
+ [](const auto& pair) { return pair.second; });
+
+ if (all_snapshots_cancelled) {
+ LOG(WARNING) << "All partitions are re-flashed after update, removing all update states.";
+ }
+ return all_snapshots_cancelled;
+}
+
+bool SnapshotManager::GetSnapshotFlashingStatus(LockedFile* lock,
+ const std::vector<std::string>& snapshots,
+ std::map<std::string, bool>* out) {
+ CHECK(lock);
+
auto source_slot_suffix = ReadUpdateSourceSlotSuffix();
if (source_slot_suffix.empty()) {
return false;
@@ -1244,20 +1268,17 @@
return false;
}
- bool all_snapshots_cancelled = true;
for (const auto& snapshot_name : snapshots) {
if (GetMetadataPartitionState(*metadata, snapshot_name) ==
MetadataPartitionState::Updated) {
- all_snapshots_cancelled = false;
- continue;
+ out->emplace(snapshot_name, false);
+ } else {
+ // Delete snapshots for partitions that are re-flashed after the update.
+ LOG(WARNING) << "Detected re-flashing of partition " << snapshot_name << ".";
+ out->emplace(snapshot_name, true);
}
- // Delete snapshots for partitions that are re-flashed after the update.
- LOG(WARNING) << "Detected re-flashing of partition " << snapshot_name << ".";
}
- if (all_snapshots_cancelled) {
- LOG(WARNING) << "All partitions are re-flashed after update, removing all update states.";
- }
- return all_snapshots_cancelled;
+ return true;
}
bool SnapshotManager::RemoveAllSnapshots(LockedFile* lock) {
@@ -1267,10 +1288,38 @@
return false;
}
+ std::map<std::string, bool> flashing_status;
+ if (!GetSnapshotFlashingStatus(lock, snapshots, &flashing_status)) {
+ LOG(WARNING) << "Failed to get flashing status";
+ }
+
+ auto current_slot = GetCurrentSlot();
bool ok = true;
bool has_mapped_cow_images = false;
for (const auto& name : snapshots) {
- if (!UnmapPartitionWithSnapshot(lock, name) || !DeleteSnapshot(lock, name)) {
+ // If booting off source slot, it is okay to unmap and delete all the snapshots.
+ // If boot indicator is missing, update state is None or Initiated, so
+ // it is also okay to unmap and delete all the snapshots.
+ // If booting off target slot,
+ // - should not unmap because:
+ // - In Android mode, snapshots are not mapped, but
+ // filesystems are mounting off dm-linear targets directly.
+ // - In recovery mode, assume nothing is mapped, so it is optional to unmap.
+ // - If partition is flashed or unknown, it is okay to delete snapshots.
+ // Otherwise (UPDATED flag), only delete snapshots if they are not mapped
+ // as dm-snapshot (for example, after merge completes).
+ bool should_unmap = current_slot != Slot::Target;
+ bool should_delete = ShouldDeleteSnapshot(lock, flashing_status, current_slot, name);
+
+ bool partition_ok = true;
+ if (should_unmap && !UnmapPartitionWithSnapshot(lock, name)) {
+ partition_ok = false;
+ }
+ if (partition_ok && should_delete && !DeleteSnapshot(lock, name)) {
+ partition_ok = false;
+ }
+
+ if (!partition_ok) {
// Remember whether or not we were able to unmap the cow image.
auto cow_image_device = GetCowImageDeviceName(name);
has_mapped_cow_images |=
@@ -1293,6 +1342,34 @@
return ok;
}
+// See comments in RemoveAllSnapshots().
+bool SnapshotManager::ShouldDeleteSnapshot(LockedFile* lock,
+ const std::map<std::string, bool>& flashing_status,
+ Slot current_slot, const std::string& name) {
+ if (current_slot != Slot::Target) {
+ return true;
+ }
+ auto it = flashing_status.find(name);
+ if (it == flashing_status.end()) {
+ LOG(WARNING) << "Can't determine flashing status for " << name;
+ return true;
+ }
+ if (it->second) {
+ // partition flashed, okay to delete obsolete snapshots
+ return true;
+ }
+ // partition updated, only delete if not dm-snapshot
+ SnapshotStatus status;
+ if (!ReadSnapshotStatus(lock, name, &status)) {
+ LOG(WARNING) << "Unable to read snapshot status for " << name
+ << ", guessing snapshot device name";
+ auto extra_name = GetSnapshotExtraDeviceName(name);
+ return !IsSnapshotDevice(name) && !IsSnapshotDevice(extra_name);
+ }
+ auto dm_name = GetSnapshotDeviceName(name, status);
+ return !IsSnapshotDevice(dm_name);
+}
+
UpdateState SnapshotManager::GetUpdateState(double* progress) {
// If we've never started an update, the state file won't exist.
auto state_file = GetStateFilePath();
@@ -1369,6 +1446,10 @@
return access(kBootIndicatorPath, F_OK) == 0;
}
+std::string SnapshotManager::GetGlobalRollbackIndicatorPath() {
+ return kRollbackIndicatorPath;
+}
+
bool SnapshotManager::NeedSnapshotsInFirstStageMount() {
// If we fail to read, we'll wind up using CreateLogicalPartitions, which
// will create devices that look like the old slot, except with extra
@@ -1379,12 +1460,14 @@
auto slot = GetCurrentSlot();
if (slot != Slot::Target) {
- if (slot == Slot::Source && !device_->IsRecovery()) {
+ if (slot == Slot::Source) {
// Device is rebooting into the original slot, so mark this as a
// rollback.
auto path = GetRollbackIndicatorPath();
if (!android::base::WriteStringToFile("1", path)) {
PLOG(ERROR) << "Unable to write rollback indicator: " << path;
+ } else {
+ LOG(INFO) << "Rollback detected, writing rollback indicator to " << path;
}
}
LOG(INFO) << "Not booting from new slot. Will not mount snapshots.";
@@ -2388,91 +2471,6 @@
return AutoUnmountDevice::New(device_->GetMetadataDir());
}
-UpdateState SnapshotManager::InitiateMergeAndWait(SnapshotMergeReport* stats_report) {
- {
- auto lock = LockExclusive();
- // Sync update state from file with bootloader.
- if (!WriteUpdateState(lock.get(), ReadUpdateState(lock.get()))) {
- LOG(WARNING) << "Unable to sync write update state, fastboot may "
- << "reject / accept wipes incorrectly!";
- }
- }
-
- SnapshotMergeStats merge_stats(*this);
-
- unsigned int last_progress = 0;
- auto callback = [&]() -> void {
- double progress;
- GetUpdateState(&progress);
- if (last_progress < static_cast<unsigned int>(progress)) {
- last_progress = progress;
- LOG(INFO) << "Waiting for merge to complete: " << last_progress << "%.";
- }
- };
-
- LOG(INFO) << "Waiting for any previous merge request to complete. "
- << "This can take up to several minutes.";
- merge_stats.Resume();
- auto state = ProcessUpdateState(callback);
- merge_stats.set_state(state);
- if (state == UpdateState::None) {
- LOG(INFO) << "Can't find any snapshot to merge.";
- return state;
- }
- if (state == UpdateState::Unverified) {
- if (GetCurrentSlot() != Slot::Target) {
- LOG(INFO) << "Cannot merge until device reboots.";
- return state;
- }
-
- // This is the first snapshot merge that is requested after OTA. We can
- // initialize the merge duration statistics.
- merge_stats.Start();
-
- if (!InitiateMerge()) {
- LOG(ERROR) << "Failed to initiate merge.";
- return state;
- }
- // All other states can be handled by ProcessUpdateState.
- LOG(INFO) << "Waiting for merge to complete. This can take up to several minutes.";
- last_progress = 0;
- state = ProcessUpdateState(callback);
- merge_stats.set_state(state);
- }
-
- LOG(INFO) << "Merge finished with state \"" << state << "\".";
- if (stats_report) {
- *stats_report = merge_stats.GetReport();
- }
- return state;
-}
-
-Return SnapshotManager::WaitForMerge() {
- LOG(INFO) << "Waiting for any previous merge request to complete. "
- << "This can take up to several minutes.";
- while (true) {
- auto state = ProcessUpdateState();
- if (state == UpdateState::Unverified && GetCurrentSlot() == Slot::Target) {
- LOG(INFO) << "Wait for merge to be initiated.";
- std::this_thread::sleep_for(kUpdateStateCheckInterval);
- continue;
- }
- LOG(INFO) << "Wait for merge exits with state " << state;
- switch (state) {
- case UpdateState::None:
- [[fallthrough]];
- case UpdateState::MergeCompleted:
- [[fallthrough]];
- case UpdateState::Cancelled:
- return Return::Ok();
- case UpdateState::MergeNeedsReboot:
- return Return::NeedsReboot();
- default:
- return Return::Error();
- }
- }
-}
-
bool SnapshotManager::HandleImminentDataWipe(const std::function<void()>& callback) {
if (!device_->IsRecovery()) {
LOG(ERROR) << "Data wipes are only allowed in recovery.";
@@ -2503,7 +2501,10 @@
return false;
}
- UpdateState state = ProcessUpdateState(callback);
+ UpdateState state = ProcessUpdateState([&]() -> bool {
+ callback();
+ return true;
+ });
LOG(INFO) << "Update state in recovery: " << state;
switch (state) {
case UpdateState::MergeFailed:
diff --git a/fs_mgr/libsnapshot/snapshot_stats.cpp b/fs_mgr/libsnapshot/snapshot_stats.cpp
index 635b47d..5da7b98 100644
--- a/fs_mgr/libsnapshot/snapshot_stats.cpp
+++ b/fs_mgr/libsnapshot/snapshot_stats.cpp
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "snapshot_stats.h"
+#include <libsnapshot/snapshot_stats.h>
#include <sstream>
@@ -23,22 +23,17 @@
namespace android {
namespace snapshot {
-SnapshotMergeStats::SnapshotMergeStats(SnapshotManager& parent) : parent_(parent) {
- init_time_ = std::chrono::steady_clock::now();
+SnapshotMergeStats* SnapshotMergeStats::GetInstance(SnapshotManager& parent) {
+ static SnapshotMergeStats g_instance(parent.GetMergeStateFilePath());
+ CHECK(g_instance.path_ == parent.GetMergeStateFilePath());
+ return &g_instance;
}
-SnapshotMergeStats::~SnapshotMergeStats() {
- std::string error;
- auto file_path = parent_.GetMergeStateFilePath();
- if (!android::base::RemoveFileIfExists(file_path, &error)) {
- LOG(ERROR) << "Failed to remove merge statistics file " << file_path << ": " << error;
- return;
- }
-}
+SnapshotMergeStats::SnapshotMergeStats(const std::string& path) : path_(path), running_(false) {}
bool SnapshotMergeStats::ReadState() {
std::string contents;
- if (!android::base::ReadFileToString(parent_.GetMergeStateFilePath(), &contents)) {
+ if (!android::base::ReadFileToString(path_, &contents)) {
PLOG(INFO) << "Read merge statistics file failed";
return false;
}
@@ -55,34 +50,73 @@
LOG(ERROR) << "Unable to serialize SnapshotMergeStats.";
return false;
}
- auto file_path = parent_.GetMergeStateFilePath();
- if (!WriteStringToFileAtomic(contents, file_path)) {
+ if (!WriteStringToFileAtomic(contents, path_)) {
PLOG(ERROR) << "Could not write to merge statistics file";
return false;
}
return true;
}
-void SnapshotMergeStats::Start() {
- report_.set_resume_count(0);
- report_.set_state(UpdateState::None);
- WriteState();
+bool SnapshotMergeStats::DeleteState() {
+ std::string error;
+ if (!android::base::RemoveFileIfExists(path_, &error)) {
+ LOG(ERROR) << "Failed to remove merge statistics file " << path_ << ": " << error;
+ return false;
+ }
+ return true;
}
-void SnapshotMergeStats::Resume() {
- if (!ReadState()) {
- return;
+bool SnapshotMergeStats::Start() {
+ if (running_) {
+ LOG(ERROR) << "SnapshotMergeStats running_ == " << running_;
+ return false;
}
- report_.set_resume_count(report_.resume_count() + 1);
- WriteState();
+ running_ = true;
+
+ start_time_ = std::chrono::steady_clock::now();
+ if (ReadState()) {
+ report_.set_resume_count(report_.resume_count() + 1);
+ } else {
+ report_.set_resume_count(0);
+ report_.set_state(UpdateState::None);
+ }
+
+ return WriteState();
}
void SnapshotMergeStats::set_state(android::snapshot::UpdateState state) {
report_.set_state(state);
}
-SnapshotMergeReport SnapshotMergeStats::GetReport() {
- return report_;
+class SnapshotMergeStatsResultImpl : public SnapshotMergeStats::Result {
+ public:
+ SnapshotMergeStatsResultImpl(const SnapshotMergeReport& report,
+ std::chrono::steady_clock::duration merge_time)
+ : report_(report), merge_time_(merge_time) {}
+ const SnapshotMergeReport& report() const override { return report_; }
+ std::chrono::steady_clock::duration merge_time() const override { return merge_time_; }
+
+ private:
+ SnapshotMergeReport report_;
+ std::chrono::steady_clock::duration merge_time_;
+};
+
+std::unique_ptr<SnapshotMergeStats::Result> SnapshotMergeStats::Finish() {
+ if (!running_) {
+ LOG(ERROR) << "SnapshotMergeStats running_ == " << running_;
+ return nullptr;
+ }
+ running_ = false;
+
+ auto result = std::make_unique<SnapshotMergeStatsResultImpl>(
+ report_, std::chrono::steady_clock::now() - start_time_);
+
+ // We still want to report result if state is not deleted. Just leave
+ // it there and move on. A side effect is that it may be reported over and
+ // over again in the future, but there is nothing we can do.
+ (void)DeleteState();
+
+ return result;
}
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapshot_stats.h b/fs_mgr/libsnapshot/snapshot_stats.h
deleted file mode 100644
index 60109a4..0000000
--- a/fs_mgr/libsnapshot/snapshot_stats.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma once
-
-#include <chrono>
-
-#include <android/snapshot/snapshot.pb.h>
-#include <libsnapshot/snapshot.h>
-
-namespace android {
-namespace snapshot {
-
-class SnapshotMergeStats {
- public:
- SnapshotMergeStats(SnapshotManager& parent);
- ~SnapshotMergeStats();
- void Start();
- void Resume();
- void set_state(android::snapshot::UpdateState state);
- SnapshotMergeReport GetReport();
-
- private:
- bool ReadState();
- bool WriteState();
-
- const SnapshotManager& parent_;
- SnapshotMergeReport report_;
- std::chrono::time_point<std::chrono::steady_clock> init_time_;
- std::chrono::time_point<std::chrono::steady_clock> end_time_;
-};
-
-} // namespace snapshot
-} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 5d2840f..855451d 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -506,9 +506,6 @@
}
TEST_F(SnapshotTest, FirstStageMountAndMerge) {
-#ifdef SKIP_TEST_IN_PRESUBMIT
- GTEST_SKIP() << "WIP failure b/148889015";
-#endif
ASSERT_TRUE(AcquireLock());
static const uint64_t kDeviceSize = 1024 * 1024;
@@ -565,9 +562,6 @@
}
TEST_F(SnapshotTest, FlashSuperDuringMerge) {
-#ifdef SKIP_TEST_IN_PRESUBMIT
- GTEST_SKIP() << "WIP failure b/148889015";
-#endif
ASSERT_TRUE(AcquireLock());
static const uint64_t kDeviceSize = 1024 * 1024;
@@ -979,9 +973,6 @@
// Also test UnmapUpdateSnapshot unmaps everything.
// Also test first stage mount and merge after this.
TEST_F(SnapshotUpdateTest, FullUpdateFlow) {
-#ifdef SKIP_TEST_IN_PRESUBMIT
- GTEST_SKIP() << "WIP failure b/148889015";
-#endif
// OTA client blindly unmaps all partitions that are possibly mapped.
for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
ASSERT_TRUE(sm->UnmapUpdateSnapshot(name));
@@ -1036,7 +1027,8 @@
}
// Initiate the merge and wait for it to be completed.
- ASSERT_EQ(UpdateState::MergeCompleted, init->InitiateMergeAndWait());
+ ASSERT_TRUE(init->InitiateMerge());
+ ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
// Check that the target partitions have the same content after the merge.
for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
@@ -1129,9 +1121,6 @@
// Test that the old partitions are not modified.
TEST_F(SnapshotUpdateTest, TestRollback) {
-#ifdef SKIP_TEST_IN_PRESUBMIT
- GTEST_SKIP() << "WIP failure b/148889015";
-#endif
// Execute the update.
ASSERT_TRUE(sm->BeginUpdate());
ASSERT_TRUE(sm->UnmapUpdateSnapshot("sys_b"));
@@ -1213,7 +1202,8 @@
// Initiate the merge and wait for it to be completed.
auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, "_b"));
- ASSERT_EQ(UpdateState::MergeCompleted, new_sm->InitiateMergeAndWait());
+ ASSERT_TRUE(new_sm->InitiateMerge());
+ ASSERT_EQ(UpdateState::MergeCompleted, new_sm->ProcessUpdateState());
// Execute the second update.
ASSERT_TRUE(new_sm->BeginUpdate());
@@ -1309,9 +1299,6 @@
}
TEST_F(SnapshotUpdateTest, MergeCannotRemoveCow) {
-#ifdef SKIP_TEST_IN_PRESUBMIT
- GTEST_SKIP() << "WIP failure b/148889015";
-#endif
// Make source partitions as big as possible to force COW image to be created.
SetSize(sys_, 5_MiB);
SetSize(vnd_, 5_MiB);
@@ -1356,7 +1343,8 @@
ASSERT_GE(fd, 0);
// COW cannot be removed due to open fd, so expect a soft failure.
- ASSERT_EQ(UpdateState::MergeNeedsReboot, init->InitiateMergeAndWait());
+ ASSERT_TRUE(init->InitiateMerge());
+ ASSERT_EQ(UpdateState::MergeNeedsReboot, init->ProcessUpdateState());
// Simulate shutting down the device.
fd.reset();
@@ -1369,7 +1357,7 @@
ASSERT_FALSE(sm->IsSnapshotDevice("sys_b", nullptr));
// Merge should be able to complete now.
- ASSERT_EQ(UpdateState::MergeCompleted, init->InitiateMergeAndWait());
+ ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
}
class MetadataMountedTest : public SnapshotUpdateTest {
@@ -1585,48 +1573,6 @@
<< "FinishedSnapshotWrites should detect overflow of CoW device.";
}
-TEST_F(SnapshotUpdateTest, WaitForMerge) {
-#ifdef SKIP_TEST_IN_PRESUBMIT
- GTEST_SKIP() << "WIP failure b/148889015";
-#endif
- AddOperationForPartitions();
-
- // Execute the update.
- ASSERT_TRUE(sm->BeginUpdate());
- ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-
- // Write some data to target partitions.
- for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
- ASSERT_TRUE(WriteSnapshotAndHash(name));
- }
-
- ASSERT_TRUE(sm->FinishedSnapshotWrites());
-
- // Simulate shutting down the device.
- ASSERT_TRUE(UnmapAll());
-
- // After reboot, init does first stage mount.
- {
- auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
- ASSERT_NE(nullptr, init);
- ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
- }
-
- auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, "_b"));
- ASSERT_NE(nullptr, new_sm);
-
- auto waiter = std::async(std::launch::async, [&new_sm] { return new_sm->WaitForMerge(); });
- ASSERT_EQ(std::future_status::timeout, waiter.wait_for(1s))
- << "WaitForMerge should block when not initiated";
-
- auto merger =
- std::async(std::launch::async, [&new_sm] { return new_sm->InitiateMergeAndWait(); });
- // Small images, so should be merged pretty quickly.
- ASSERT_EQ(std::future_status::ready, waiter.wait_for(3s)) << "WaitForMerge did not finish";
- ASSERT_TRUE(waiter.get());
- ASSERT_THAT(merger.get(), AnyOf(UpdateState::None, UpdateState::MergeCompleted));
-}
-
TEST_F(SnapshotUpdateTest, LowSpace) {
static constexpr auto kMaxFree = 10_MiB;
auto userdata = std::make_unique<LowSpaceUserdata>();
@@ -1748,7 +1694,8 @@
// There should be no snapshot to merge.
auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, flashed_slot_suffix));
- ASSERT_EQ(UpdateState::Cancelled, new_sm->InitiateMergeAndWait());
+ // update_enigne calls ProcessUpdateState first -- should see Cancelled.
+ ASSERT_EQ(UpdateState::Cancelled, new_sm->ProcessUpdateState());
// Next OTA calls CancelUpdate no matter what.
ASSERT_TRUE(new_sm->CancelUpdate());
diff --git a/fs_mgr/libsnapshot/snapshotctl.cpp b/fs_mgr/libsnapshot/snapshotctl.cpp
index 34d3d69..a44de84 100644
--- a/fs_mgr/libsnapshot/snapshotctl.cpp
+++ b/fs_mgr/libsnapshot/snapshotctl.cpp
@@ -24,12 +24,8 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
-#include <android/snapshot/snapshot.pb.h>
-#include <libsnapshot/snapshot.h>
-#include <statslog.h>
-#include "snapshot_stats.h"
-#include "utility.h"
+#include <libsnapshot/snapshot.h>
using namespace std::string_literals;
@@ -39,145 +35,22 @@
"Actions:\n"
" dump\n"
" Print snapshot states.\n"
- " merge [--logcat] [--log-to-file] [--report] [--dry-run]\n"
- " Initialize merge and wait for it to be completed.\n"
- " If --logcat is specified, log to logcat.\n"
- " If --log-to-file is specified, log to /data/misc/snapshotctl_log/.\n"
- " If both specified, log to both. If none specified, log to stdout.\n"
- " If --report is specified, send merge statistics to statsd.\n"
- " If --dry-run flag, no real merge operation is is triggered, and\n"
- " sample statistics are sent to statsd for testing purpose.\n";
+ " merge\n"
+ " Deprecated.\n";
return EX_USAGE;
}
namespace android {
namespace snapshot {
-static SnapshotMergeReport GetDummySnapshotMergeReport() {
- SnapshotMergeReport fake_report;
-
- fake_report.set_state(UpdateState::MergeCompleted);
- fake_report.set_resume_count(56);
-
- return fake_report;
-}
-
bool DumpCmdHandler(int /*argc*/, char** argv) {
android::base::InitLogging(argv, &android::base::StderrLogger);
return SnapshotManager::New()->Dump(std::cout);
}
-class FileLogger {
- public:
- FileLogger() {
- static constexpr const char* kLogFilePath = "/data/misc/snapshotctl_log/";
- std::stringstream ss;
- ss << kLogFilePath << "snapshotctl." << Now() << ".log";
- fd_.reset(TEMP_FAILURE_RETRY(
- open(ss.str().c_str(),
- O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW | O_SYNC, 0644)));
- if (fd_ == -1) {
- PLOG(ERROR) << "Cannot open persistent log " << ss.str();
- return;
- }
- // Explicitly chmod again because mode in open() may be masked by umask.
- if (fchmod(fd_.get(), 0644) == -1) {
- PLOG(ERROR) << "Cannot chmod 0644 persistent log " << ss.str();
- return;
- }
- }
- // Copy-contuctor needed to be converted to std::function.
- FileLogger(const FileLogger& other) { fd_.reset(dup(other.fd_)); }
- void operator()(android::base::LogId, android::base::LogSeverity, const char* /*tag*/,
- const char* /*file*/, unsigned int /*line*/, const char* message) {
- if (fd_ == -1) return;
- std::stringstream ss;
- ss << Now() << ":" << message << "\n";
- (void)android::base::WriteStringToFd(ss.str(), fd_);
- }
-
- private:
- android::base::unique_fd fd_;
-};
-
-class MergeCmdLogger {
- public:
- MergeCmdLogger(int argc, char** argv) {
- for (int i = 0; i < argc; ++i) {
- if (argv[i] == "--logcat"s) {
- loggers_.push_back(android::base::LogdLogger());
- }
- if (argv[i] == "--log-to-file"s) {
- loggers_.push_back(std::move(FileLogger()));
- }
- }
- if (loggers_.empty()) {
- loggers_.push_back(&android::base::StdioLogger);
- }
- }
- void operator()(android::base::LogId id, android::base::LogSeverity severity, const char* tag,
- const char* file, unsigned int line, const char* message) {
- for (auto&& logger : loggers_) {
- logger(id, severity, tag, file, line, message);
- }
- }
-
- private:
- std::vector<android::base::LogFunction> loggers_;
-};
-
-bool MergeCmdHandler(int argc, char** argv) {
- std::chrono::milliseconds passed_ms;
-
- bool report_to_statsd = false;
- bool dry_run = false;
- for (int i = 2; i < argc; ++i) {
- if (argv[i] == "--report"s) {
- report_to_statsd = true;
- } else if (argv[i] == "--dry-run"s) {
- dry_run = true;
- }
- }
-
- // 'snapshotctl merge' is stripped away from arguments to
- // Logger.
- android::base::InitLogging(argv);
- android::base::SetLogger(MergeCmdLogger(argc - 2, argv + 2));
-
- UpdateState state;
- SnapshotMergeReport merge_report;
- if (dry_run) {
- merge_report = GetDummySnapshotMergeReport();
- state = merge_report.state();
- passed_ms = std::chrono::milliseconds(1234);
- } else {
- auto begin = std::chrono::steady_clock::now();
-
- state = SnapshotManager::New()->InitiateMergeAndWait(&merge_report);
-
- // We could wind up in the Unverified state if the device rolled back or
- // hasn't fully rebooted. Ignore this.
- if (state == UpdateState::None || state == UpdateState::Unverified) {
- return true;
- }
-
- auto end = std::chrono::steady_clock::now();
- passed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin);
- }
-
- if (report_to_statsd) {
- android::util::stats_write(android::util::SNAPSHOT_MERGE_REPORTED,
- static_cast<int32_t>(merge_report.state()),
- static_cast<int64_t>(passed_ms.count()),
- static_cast<int32_t>(merge_report.resume_count()));
- }
-
- if (state == UpdateState::MergeCompleted) {
- LOG(INFO) << "Snapshot merged in " << passed_ms.count() << " ms.";
- return true;
- }
-
- LOG(ERROR) << "Snapshot failed to merge with state \"" << state << "\".";
+bool MergeCmdHandler(int /*argc*/, char** argv) {
+ android::base::InitLogging(argv, &android::base::StderrLogger);
+ LOG(WARNING) << "Deprecated. Call update_engine_client --merge instead.";
return false;
}
diff --git a/fs_mgr/libsnapshot/snapshotctl.rc b/fs_mgr/libsnapshot/snapshotctl.rc
deleted file mode 100644
index 5dbe352..0000000
--- a/fs_mgr/libsnapshot/snapshotctl.rc
+++ /dev/null
@@ -1,2 +0,0 @@
-on property:sys.boot_completed=1
- exec_background - root root -- /system/bin/snapshotctl merge --logcat --log-to-file
diff --git a/fs_mgr/libsnapshot/utility.cpp b/fs_mgr/libsnapshot/utility.cpp
index 3318b33..d32b61e 100644
--- a/fs_mgr/libsnapshot/utility.cpp
+++ b/fs_mgr/libsnapshot/utility.cpp
@@ -34,6 +34,7 @@
using android::fs_mgr::MetadataBuilder;
using android::fs_mgr::Partition;
using android::fs_mgr::ReadDefaultFstab;
+using google::protobuf::RepeatedPtrField;
namespace android {
namespace snapshot {
@@ -166,5 +167,20 @@
return os << std::put_time(&now, "%Y%m%d-%H%M%S");
}
+void AppendExtent(RepeatedPtrField<chromeos_update_engine::Extent>* extents, uint64_t start_block,
+ uint64_t num_blocks) {
+ if (extents->size() > 0) {
+ auto last_extent = extents->rbegin();
+ auto next_block = last_extent->start_block() + last_extent->num_blocks();
+ if (start_block == next_block) {
+ last_extent->set_num_blocks(last_extent->num_blocks() + num_blocks);
+ return;
+ }
+ }
+ auto* new_extent = extents->Add();
+ new_extent->set_start_block(start_block);
+ new_extent->set_num_blocks(num_blocks);
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/utility.h b/fs_mgr/libsnapshot/utility.h
index 90ad0fe..e69bdad 100644
--- a/fs_mgr/libsnapshot/utility.h
+++ b/fs_mgr/libsnapshot/utility.h
@@ -125,5 +125,9 @@
struct Now {};
std::ostream& operator<<(std::ostream& os, const Now&);
+// Append to |extents|. Merged into the last element if possible.
+void AppendExtent(google::protobuf::RepeatedPtrField<chromeos_update_engine::Extent>* extents,
+ uint64_t start_block, uint64_t num_blocks);
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index 9caae35..16e38f1 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -27,6 +27,7 @@
#include <android-base/file.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
+#include <fs_mgr.h>
#include <fstab/fstab.h>
#include <gtest/gtest.h>
@@ -1001,6 +1002,10 @@
}
Fstab fstab;
ASSERT_TRUE(ReadDefaultFstab(&fstab)) << "Failed to read default fstab";
- ASSERT_NE(nullptr, GetMountedEntryForUserdata(&fstab))
+ Fstab proc_mounts;
+ ASSERT_TRUE(ReadFstabFromFile("/proc/mounts", &proc_mounts)) << "Failed to read /proc/mounts";
+ auto mounted_entry = GetEntryForMountPoint(&proc_mounts, "/data");
+ ASSERT_NE(mounted_entry, nullptr) << "/data is not mounted";
+ ASSERT_NE(nullptr, fs_mgr_get_mounted_entry_for_userdata(&fstab, *mounted_entry))
<< "/data wasn't mounted from default fstab";
}
diff --git a/init/init.cpp b/init/init.cpp
index bfef9e5..b0f929c 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -778,11 +778,10 @@
if (false) DumpState();
// Make the GSI status available before scripts start running.
- if (android::gsi::IsGsiRunning()) {
- SetProperty("ro.gsid.image_running", "1");
- } else {
- SetProperty("ro.gsid.image_running", "0");
- }
+ auto is_running = android::gsi::IsGsiRunning() ? "1" : "0";
+ SetProperty(gsi::kGsiBootedProp, is_running);
+ auto is_installed = android::gsi::IsGsiInstalled() ? "1" : "0";
+ SetProperty(gsi::kGsiInstalledProp, is_installed);
am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index 2175075..aa36849 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -323,20 +323,10 @@
}
if (bootstrap_ns_id != GetMountNamespaceId() && bootstrap_ns_fd.get() != -1 &&
IsApexUpdatable()) {
- // The property service thread and its descendent threads must be in the correct mount
- // namespace to call Service::Start(), however setns() only operates on a single thread and
- // fails when secondary threads attempt to join the same mount namespace. Therefore, we
- // must join the property service thread and its descendents before the setns() call. Those
- // threads are then started again after the setns() call, and they'll be in the proper
- // namespace.
- PausePropertyService();
-
if (setns(bootstrap_ns_fd.get(), CLONE_NEWNS) == -1) {
PLOG(ERROR) << "Failed to switch to bootstrap mount namespace.";
return false;
}
-
- ResumePropertyService();
}
return true;
}
diff --git a/init/selinux.cpp b/init/selinux.cpp
index c5b7576..acbcbd6 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -66,6 +66,7 @@
#include <android-base/unique_fd.h>
#include <fs_avb/fs_avb.h>
#include <libgsi/libgsi.h>
+#include <libsnapshot/snapshot.h>
#include <selinux/android.h>
#include "debug_ramdisk.h"
@@ -78,6 +79,7 @@
using android::base::Timer;
using android::base::unique_fd;
using android::fs_mgr::AvbHandle;
+using android::snapshot::SnapshotManager;
namespace android {
namespace init {
@@ -535,7 +537,11 @@
selinux_android_restorecon("/linkerconfig", 0);
- selinux_android_restorecon(gsi::kDsuAvbKeyDir, SELINUX_ANDROID_RESTORECON_RECURSE);
+ // adb remount, snapshot-based updates, and DSUs all create files during
+ // first-stage init.
+ selinux_android_restorecon("/metadata", SELINUX_ANDROID_RESTORECON_RECURSE);
+
+ selinux_android_restorecon(SnapshotManager::GetGlobalRollbackIndicatorPath().c_str(), 0);
}
int SelinuxKlogCallback(int type, const char* fmt, ...) {
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index 565f2c3..59bd97c 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -89,6 +89,9 @@
},
android: {
static_libs: ["libasync_safe"],
+ static: {
+ whole_static_libs: ["libasync_safe"],
+ },
},
vendor: {
cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 4e93df3..fc06c1d 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -45,13 +45,9 @@
}
// Socket specific parts of libcutils that are safe to statically link into an APEX.
-cc_library_static {
+cc_library {
name: "libcutils_sockets",
vendor_available: true,
- vndk: {
- enabled: true,
- support_system_process: true,
- },
recovery_available: true,
host_supported: true,
native_bridge_supported: true,
@@ -62,6 +58,7 @@
export_include_dirs: ["include"],
+ shared_libs: ["liblog"],
srcs: ["sockets.cpp"],
target: {
linux_bionic: {
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index c4e4f85..5805a4d 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -201,6 +201,8 @@
CAP_MASK_LONG(CAP_SETGID),
"system/bin/simpleperf_app_runner" },
{ 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/e2fsck" },
+ { 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/tune2fs" },
+ { 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/resize2fs" },
// generic defaults
{ 00755, AID_ROOT, AID_ROOT, 0, "bin/*" },
{ 00640, AID_ROOT, AID_SHELL, 0, "fstab.*" },
diff --git a/liblog/Android.bp b/liblog/Android.bp
index f1e5118..8410370 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -117,8 +117,12 @@
logtags: ["event.logtags"],
compile_multilib: "both",
apex_available: [
- "//apex_available:anyapex",
"//apex_available:platform",
+ // liblog is exceptionally available to the runtime APEX
+ // because the dynamic linker has to use it statically.
+ // See b/151051671
+ "com.android.runtime",
+ // DO NOT add more apex names here
],
}
diff --git a/liblog/include/android/log.h b/liblog/include/android/log.h
index c98455d..1c4ec64 100644
--- a/liblog/include/android/log.h
+++ b/liblog/include/android/log.h
@@ -56,6 +56,11 @@
#include <stdarg.h>
#include <stddef.h>
+#include <sys/cdefs.h>
+
+#if !defined(__BIONIC__) && !defined(__INTRODUCED_IN)
+#define __INTRODUCED_IN(x)
+#endif
#ifdef __cplusplus
extern "C" {
@@ -191,6 +196,18 @@
};
/**
+ * Prototype for the 'logger' function that is called for every log message.
+ */
+typedef void (*__android_logger_function)(const struct __android_logger_data* logger_data,
+ const char* message);
+/**
+ * Prototype for the 'abort' function that is called when liblog will abort due to
+ * __android_log_assert() failures.
+ */
+typedef void (*__android_aborter_function)(const char* abort_message);
+
+#if __ANDROID_API__ >= 30 || !defined(__ANDROID__)
+/**
* Writes the log message specified with logger_data and msg to the log. logger_data includes
* additional file name and line number information that a logger may use. logger_data is versioned
* for backwards compatibility.
@@ -199,54 +216,44 @@
* buffers, then pass the message to liblog via this function, and therefore we do not want to
* duplicate the loggability check here.
*/
-void __android_log_write_logger_data(struct __android_logger_data* logger_data, const char* msg);
-
-/**
- * Prototype for the 'logger' function that is called for every log message.
- */
-typedef void (*__android_logger_function)(const struct __android_logger_data* logger_data,
- const char* message);
+void __android_log_write_logger_data(struct __android_logger_data* logger_data, const char* msg)
+ __INTRODUCED_IN(30);
/**
* Sets a user defined logger function. All log messages sent to liblog will be set to the
* function pointer specified by logger for processing.
*/
-void __android_log_set_logger(__android_logger_function logger);
+void __android_log_set_logger(__android_logger_function logger) __INTRODUCED_IN(30);
/**
* Writes the log message to logd. This is an __android_logger_function and can be provided to
* __android_log_set_logger(). It is the default logger when running liblog on a device.
*/
-void __android_log_logd_logger(const struct __android_logger_data* logger_data, const char* msg);
+void __android_log_logd_logger(const struct __android_logger_data* logger_data, const char* msg)
+ __INTRODUCED_IN(30);
/**
* Writes the log message to stderr. This is an __android_logger_function and can be provided to
* __android_log_set_logger(). It is the default logger when running liblog on host.
*/
void __android_log_stderr_logger(const struct __android_logger_data* logger_data,
- const char* message);
-
-/**
- * Prototype for the 'abort' function that is called when liblog will abort due to
- * __android_log_assert() failures.
- */
-typedef void (*__android_aborter_function)(const char* abort_message);
+ const char* message) __INTRODUCED_IN(30);
/**
* Sets a user defined aborter function that is called for __android_log_assert() failures.
*/
-void __android_log_set_aborter(__android_aborter_function aborter);
+void __android_log_set_aborter(__android_aborter_function aborter) __INTRODUCED_IN(30);
/**
* Calls the stored aborter function. This allows for other logging libraries to use the same
* aborter function by calling this function in liblog.
*/
-void __android_log_call_aborter(const char* abort_message);
+void __android_log_call_aborter(const char* abort_message) __INTRODUCED_IN(30);
/**
* Sets android_set_abort_message() on device then aborts(). This is the default aborter.
*/
-void __android_log_default_aborter(const char* abort_message);
+void __android_log_default_aborter(const char* abort_message) __INTRODUCED_IN(30);
/**
* Use the per-tag properties "log.tag.<tagname>" along with the minimum priority from
@@ -260,28 +267,30 @@
*
* prio is ANDROID_LOG_VERBOSE to ANDROID_LOG_FATAL.
*/
-int __android_log_is_loggable(int prio, const char* tag, int default_prio);
-int __android_log_is_loggable_len(int prio, const char* tag, size_t len, int default_prio);
+int __android_log_is_loggable(int prio, const char* tag, int default_prio) __INTRODUCED_IN(30);
+int __android_log_is_loggable_len(int prio, const char* tag, size_t len, int default_prio)
+ __INTRODUCED_IN(30);
/**
* Sets the minimum priority that will be logged for this process.
*
* This returns the previous set minimum priority, or ANDROID_LOG_DEFAULT if none was set.
*/
-int __android_log_set_minimum_priority(int priority);
+int __android_log_set_minimum_priority(int priority) __INTRODUCED_IN(30);
/**
* Gets the minimum priority that will be logged for this process. If none has been set by a
* previous __android_log_set_minimum_priority() call, this returns ANDROID_LOG_DEFAULT.
*/
-int __android_log_get_minimum_priority(void);
+int __android_log_get_minimum_priority(void) __INTRODUCED_IN(30);
/**
* Sets the default tag if no tag is provided when writing a log message. Defaults to
* getprogname(). This truncates tag to the maximum log message size, though appropriate tags
* should be much smaller.
*/
-void __android_log_set_default_tag(const char* tag);
+void __android_log_set_default_tag(const char* tag) __INTRODUCED_IN(30);
+#endif
#ifdef __cplusplus
}
diff --git a/liblog/logger_write.cpp b/liblog/logger_write.cpp
index cf82e0f..454a13b 100644
--- a/liblog/logger_write.cpp
+++ b/liblog/logger_write.cpp
@@ -27,6 +27,7 @@
#include <android/set_abort_message.h>
#endif
+#include <atomic>
#include <shared_mutex>
#include <android-base/errno_restorer.h>
@@ -148,11 +149,9 @@
GetDefaultTag().assign(tag, 0, LOGGER_ENTRY_MAX_PAYLOAD);
}
-static int minimum_log_priority = ANDROID_LOG_DEFAULT;
+static std::atomic_int minimum_log_priority = ANDROID_LOG_DEFAULT;
int __android_log_set_minimum_priority(int priority) {
- int old_minimum_log_priority = minimum_log_priority;
- minimum_log_priority = priority;
- return old_minimum_log_priority;
+ return minimum_log_priority.exchange(priority, std::memory_order_relaxed);
}
int __android_log_get_minimum_priority() {
diff --git a/libstats/push_compat/Android.bp b/libstats/push_compat/Android.bp
index 3e69556..f0fcff6 100644
--- a/libstats/push_compat/Android.bp
+++ b/libstats/push_compat/Android.bp
@@ -35,8 +35,9 @@
header_libs: ["libstatssocket_headers"],
static_libs: [
"libbase",
+ ],
+ shared_libs: [
"liblog",
- "libutils",
],
}
diff --git a/libstats/push_compat/StatsEventCompat.cpp b/libstats/push_compat/StatsEventCompat.cpp
index de458b3..e1a86ae 100644
--- a/libstats/push_compat/StatsEventCompat.cpp
+++ b/libstats/push_compat/StatsEventCompat.cpp
@@ -15,12 +15,16 @@
*/
#include "include/StatsEventCompat.h"
+
+#include <chrono>
+
+#include <android-base/chrono_utils.h>
#include <android-base/properties.h>
#include <android/api-level.h>
#include <android/log.h>
#include <dlfcn.h>
-#include <utils/SystemClock.h>
+using android::base::boot_clock;
using android::base::GetProperty;
const static int kStatsEventTag = 1937006964;
@@ -36,21 +40,26 @@
GetProperty("ro.build.version.codename", "") == "R" ||
android_get_device_api_level() > __ANDROID_API_Q__;
-// definitions of static class variables
+// initializations of static class variables
bool StatsEventCompat::mAttemptedLoad = false;
-void* StatsEventCompat::mStatsEventApi = nullptr;
std::mutex StatsEventCompat::mLoadLock;
+AStatsEventApi StatsEventCompat::mAStatsEventApi;
+
+static int64_t elapsedRealtimeNano() {
+ return std::chrono::time_point_cast<std::chrono::nanoseconds>(boot_clock::now())
+ .time_since_epoch()
+ .count();
+}
StatsEventCompat::StatsEventCompat() : mEventQ(kStatsEventTag) {
// guard loading because StatsEventCompat might be called from multithreaded
// environment
{
std::lock_guard<std::mutex> lg(mLoadLock);
- if (!mAttemptedLoad) {
+ if (!mAttemptedLoad && mPlatformAtLeastR) {
void* handle = dlopen("libstatssocket.so", RTLD_NOW);
if (handle) {
- // mStatsEventApi = (struct AStatsEvent_apiTable*)dlsym(handle,
- // "table");
+ initializeApiTableLocked(handle);
} else {
ALOGE("dlopen failed: %s\n", dlerror());
}
@@ -58,61 +67,93 @@
mAttemptedLoad = true;
}
- if (mStatsEventApi) {
- // mEventR = mStatsEventApi->obtain();
- } else if (!mPlatformAtLeastR) {
- mEventQ << android::elapsedRealtimeNano();
+ if (useRSchema()) {
+ mEventR = mAStatsEventApi.obtain();
+ } else if (useQSchema()) {
+ mEventQ << elapsedRealtimeNano();
}
}
StatsEventCompat::~StatsEventCompat() {
- // if (mStatsEventApi) mStatsEventApi->release(mEventR);
+ if (useRSchema()) mAStatsEventApi.release(mEventR);
+}
+
+// Populates the AStatsEventApi struct by calling dlsym to find the address of
+// each API function.
+void StatsEventCompat::initializeApiTableLocked(void* handle) {
+ mAStatsEventApi.obtain = (AStatsEvent* (*)())dlsym(handle, "AStatsEvent_obtain");
+ mAStatsEventApi.build = (void (*)(AStatsEvent*))dlsym(handle, "AStatsEvent_build");
+ mAStatsEventApi.write = (int (*)(AStatsEvent*))dlsym(handle, "AStatsEvent_write");
+ mAStatsEventApi.release = (void (*)(AStatsEvent*))dlsym(handle, "AStatsEvent_release");
+ mAStatsEventApi.setAtomId =
+ (void (*)(AStatsEvent*, uint32_t))dlsym(handle, "AStatsEvent_setAtomId");
+ mAStatsEventApi.writeInt32 =
+ (void (*)(AStatsEvent*, int32_t))dlsym(handle, "AStatsEvent_writeInt32");
+ mAStatsEventApi.writeInt64 =
+ (void (*)(AStatsEvent*, int64_t))dlsym(handle, "AStatsEvent_writeInt64");
+ mAStatsEventApi.writeFloat =
+ (void (*)(AStatsEvent*, float))dlsym(handle, "AStatsEvent_writeFloat");
+ mAStatsEventApi.writeBool =
+ (void (*)(AStatsEvent*, bool))dlsym(handle, "AStatsEvent_writeBool");
+ mAStatsEventApi.writeByteArray = (void (*)(AStatsEvent*, const uint8_t*, size_t))dlsym(
+ handle, "AStatsEvent_writeByteArray");
+ mAStatsEventApi.writeString =
+ (void (*)(AStatsEvent*, const char*))dlsym(handle, "AStatsEvent_writeString");
+ mAStatsEventApi.writeAttributionChain =
+ (void (*)(AStatsEvent*, const uint32_t*, const char* const*, uint8_t))dlsym(
+ handle, "AStatsEvent_writeAttributionChain");
+ mAStatsEventApi.addBoolAnnotation =
+ (void (*)(AStatsEvent*, uint8_t, bool))dlsym(handle, "AStatsEvent_addBoolAnnotation");
+ mAStatsEventApi.addInt32Annotation = (void (*)(AStatsEvent*, uint8_t, int32_t))dlsym(
+ handle, "AStatsEvent_addInt32Annotation");
+
+ mAStatsEventApi.initialized = true;
}
void StatsEventCompat::setAtomId(int32_t atomId) {
- if (mStatsEventApi) {
- // mStatsEventApi->setAtomId(mEventR, (uint32_t)atomId);
- } else if (!mPlatformAtLeastR) {
+ if (useRSchema()) {
+ mAStatsEventApi.setAtomId(mEventR, (uint32_t)atomId);
+ } else if (useQSchema()) {
mEventQ << atomId;
}
}
void StatsEventCompat::writeInt32(int32_t value) {
- if (mStatsEventApi) {
- // mStatsEventApi->writeInt32(mEventR, value);
- } else if (!mPlatformAtLeastR) {
+ if (useRSchema()) {
+ mAStatsEventApi.writeInt32(mEventR, value);
+ } else if (useQSchema()) {
mEventQ << value;
}
}
void StatsEventCompat::writeInt64(int64_t value) {
- if (mStatsEventApi) {
- // mStatsEventApi->writeInt64(mEventR, value);
- } else if (!mPlatformAtLeastR) {
+ if (useRSchema()) {
+ mAStatsEventApi.writeInt64(mEventR, value);
+ } else if (useQSchema()) {
mEventQ << value;
}
}
void StatsEventCompat::writeFloat(float value) {
- if (mStatsEventApi) {
- // mStatsEventApi->writeFloat(mEventR, value);
- } else if (!mPlatformAtLeastR) {
+ if (useRSchema()) {
+ mAStatsEventApi.writeFloat(mEventR, value);
+ } else if (useQSchema()) {
mEventQ << value;
}
}
void StatsEventCompat::writeBool(bool value) {
- if (mStatsEventApi) {
- // mStatsEventApi->writeBool(mEventR, value);
- } else if (!mPlatformAtLeastR) {
+ if (useRSchema()) {
+ mAStatsEventApi.writeBool(mEventR, value);
+ } else if (useQSchema()) {
mEventQ << value;
}
}
void StatsEventCompat::writeByteArray(const char* buffer, size_t length) {
- if (mStatsEventApi) {
- // mStatsEventApi->writeByteArray(mEventR, (const uint8_t*)buffer, length);
- } else if (!mPlatformAtLeastR) {
+ if (useRSchema()) {
+ mAStatsEventApi.writeByteArray(mEventR, reinterpret_cast<const uint8_t*>(buffer), length);
+ } else if (useQSchema()) {
mEventQ.AppendCharArray(buffer, length);
}
}
@@ -120,19 +161,19 @@
void StatsEventCompat::writeString(const char* value) {
if (value == nullptr) value = "";
- if (mStatsEventApi) {
- // mStatsEventApi->writeString(mEventR, value);
- } else if (!mPlatformAtLeastR) {
+ if (useRSchema()) {
+ mAStatsEventApi.writeString(mEventR, value);
+ } else if (useQSchema()) {
mEventQ << value;
}
}
void StatsEventCompat::writeAttributionChain(const int32_t* uids, size_t numUids,
const vector<const char*>& tags) {
- if (mStatsEventApi) {
- // mStatsEventApi->writeAttributionChain(mEventR, (const uint32_t*)uids, tags.data(),
- // (uint8_t)numUids);
- } else if (!mPlatformAtLeastR) {
+ if (useRSchema()) {
+ mAStatsEventApi.writeAttributionChain(mEventR, (const uint32_t*)uids, tags.data(),
+ (uint8_t)numUids);
+ } else if (useQSchema()) {
mEventQ.begin();
for (size_t i = 0; i < numUids; i++) {
mEventQ.begin();
@@ -149,8 +190,8 @@
const map<int, int64_t>& int64Map,
const map<int, const char*>& stringMap,
const map<int, float>& floatMap) {
- // Key value pairs are not supported with AStatsEvent.
- if (!mPlatformAtLeastR) {
+ // AStatsEvent does not support key value pairs.
+ if (useQSchema()) {
mEventQ.begin();
writeKeyValuePairMap(int32Map);
writeKeyValuePairMap(int64Map);
@@ -177,34 +218,36 @@
template void StatsEventCompat::writeKeyValuePairMap<const char*>(const map<int, const char*>&);
void StatsEventCompat::addBoolAnnotation(uint8_t annotationId, bool value) {
- // Workaround for unused params.
- (void)annotationId;
- (void)value;
- // if (mStatsEventApi) mStatsEventApi->addBoolAnnotation(mEventR, annotationId, value);
+ if (useRSchema()) {
+ mAStatsEventApi.addBoolAnnotation(mEventR, annotationId, value);
+ }
// Don't do anything if on Q.
}
void StatsEventCompat::addInt32Annotation(uint8_t annotationId, int32_t value) {
- // Workaround for unused params.
- (void)annotationId;
- (void)value;
- // if (mStatsEventApi) mStatsEventApi->addInt32Annotation(mEventR, annotationId, value);
+ if (useRSchema()) {
+ mAStatsEventApi.addInt32Annotation(mEventR, annotationId, value);
+ }
// Don't do anything if on Q.
}
int StatsEventCompat::writeToSocket() {
- if (mStatsEventApi) {
- // mStatsEventApi->build(mEventR);
- // return mStatsEventApi->write(mEventR);
+ if (useRSchema()) {
+ mAStatsEventApi.build(mEventR);
+ return mAStatsEventApi.write(mEventR);
}
- if (!mPlatformAtLeastR) return mEventQ.write(LOG_ID_STATS);
+ if (useQSchema()) return mEventQ.write(LOG_ID_STATS);
- // We reach here only if we're on R, but libstatspush_compat was unable to
+ // We reach here only if we're on R, but libstatssocket was unable to
// be loaded using dlopen.
return -ENOLINK;
}
-bool StatsEventCompat::usesNewSchema() {
- return mStatsEventApi != nullptr;
+bool StatsEventCompat::useRSchema() {
+ return mPlatformAtLeastR && mAStatsEventApi.initialized;
+}
+
+bool StatsEventCompat::useQSchema() {
+ return !mPlatformAtLeastR;
}
diff --git a/libstats/push_compat/include/StatsEventCompat.h b/libstats/push_compat/include/StatsEventCompat.h
index ad423a1..00bf48b 100644
--- a/libstats/push_compat/include/StatsEventCompat.h
+++ b/libstats/push_compat/include/StatsEventCompat.h
@@ -26,6 +26,26 @@
using std::map;
using std::vector;
+struct AStatsEventApi {
+ // Indicates whether the below function pointers have been set using dlsym.
+ bool initialized = false;
+
+ AStatsEvent* (*obtain)(void);
+ void (*build)(AStatsEvent*);
+ int (*write)(AStatsEvent*);
+ void (*release)(AStatsEvent*);
+ void (*setAtomId)(AStatsEvent*, uint32_t);
+ void (*writeInt32)(AStatsEvent*, int32_t);
+ void (*writeInt64)(AStatsEvent*, int64_t);
+ void (*writeFloat)(AStatsEvent*, float);
+ void (*writeBool)(AStatsEvent*, bool);
+ void (*writeByteArray)(AStatsEvent*, const uint8_t*, size_t);
+ void (*writeString)(AStatsEvent*, const char*);
+ void (*writeAttributionChain)(AStatsEvent*, const uint32_t*, const char* const*, uint8_t);
+ void (*addBoolAnnotation)(AStatsEvent*, uint8_t, bool);
+ void (*addInt32Annotation)(AStatsEvent*, uint8_t, int32_t);
+};
+
class StatsEventCompat {
public:
StatsEventCompat();
@@ -57,8 +77,7 @@
const static bool mPlatformAtLeastR;
static bool mAttemptedLoad;
static std::mutex mLoadLock;
- // static struct AStatsEvent_apiTable* mStatsEventApi;
- static void* mStatsEventApi;
+ static AStatsEventApi mAStatsEventApi;
// non-static member variables
AStatsEvent* mEventR = nullptr;
@@ -67,6 +86,9 @@
template <class T>
void writeKeyValuePairMap(const map<int, T>& keyValuePairMap);
- bool usesNewSchema();
+ void initializeApiTableLocked(void* handle);
+ bool useRSchema();
+ bool useQSchema();
+
FRIEND_TEST(StatsEventCompatTest, TestDynamicLoading);
};
diff --git a/libstats/push_compat/tests/StatsEventCompat_test.cpp b/libstats/push_compat/tests/StatsEventCompat_test.cpp
index 2be24ec..dcb3797 100644
--- a/libstats/push_compat/tests/StatsEventCompat_test.cpp
+++ b/libstats/push_compat/tests/StatsEventCompat_test.cpp
@@ -29,10 +29,10 @@
*
* TODO(b/146019024): migrate to android_get_device_api_level()
*/
-const static bool mPlatformAtLeastR = GetProperty("ro.build.version.release", "") == "R" ||
+const static bool mPlatformAtLeastR = GetProperty("ro.build.version.codename", "") == "R" ||
android_get_device_api_level() > __ANDROID_API_Q__;
TEST(StatsEventCompatTest, TestDynamicLoading) {
StatsEventCompat event;
- EXPECT_EQ(mPlatformAtLeastR, event.usesNewSchema());
+ EXPECT_EQ(mPlatformAtLeastR, event.useRSchema());
}
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 4cb4c8b..201fb12 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -706,9 +706,7 @@
# A tmpfs directory, which will contain all apps CE DE data directory that
# bind mount from the original source.
- chown root root /data_mirror
- chmod 0700 /data_mirror
- mount tmpfs tmpfs /data_mirror mode=0700,uid=0,gid=1000 nodev noexec nosuid
+ mount tmpfs tmpfs /data_mirror nodev noexec nosuid mode=0700,uid=0,gid=1000
restorecon /data_mirror
mkdir /data_mirror/data_ce 0700 root root
mkdir /data_mirror/data_de 0700 root root