Merge "TEST_MAPPING: add the rest of our actively maintained tests."
diff --git a/adb/Android.bp b/adb/Android.bp
index 00e98fe..e994075 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -285,6 +285,15 @@
"deploypatchgenerator",
],
+ // Archive adb, adb.exe.
+ dist: {
+ targets: [
+ "dist_files",
+ "sdk",
+ "win_sdk",
+ ],
+ },
+
target: {
darwin: {
cflags: [
diff --git a/adb/Android.mk b/adb/Android.mk
deleted file mode 100644
index 8b2d558..0000000
--- a/adb/Android.mk
+++ /dev/null
@@ -1,8 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-# Archive adb, adb.exe.
-$(call dist-for-goals,dist_files sdk win_sdk,$(HOST_OUT_EXECUTABLES)/adb)
-
-ifdef HOST_CROSS_OS
-$(call dist-for-goals,dist_files sdk win_sdk,$(ALL_MODULES.host_cross_adb.BUILT))
-endif
diff --git a/adb/adb.h b/adb/adb.h
index 8c37c4b..e2911e8 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -59,7 +59,7 @@
std::string adb_version();
// Increment this when we want to force users to start a new adb server.
-#define ADB_SERVER_VERSION 41
+#define ADB_SERVER_VERSION 40
using TransportId = uint64_t;
class atransport;
diff --git a/adb/adb_auth.h b/adb/adb_auth.h
index 715e04f..2fc8478 100644
--- a/adb/adb_auth.h
+++ b/adb/adb_auth.h
@@ -36,6 +36,7 @@
void adb_auth_init();
int adb_auth_keygen(const char* filename);
+int adb_auth_pubkey(const char* filename);
std::string adb_auth_get_userkey();
std::deque<std::shared_ptr<RSA>> adb_auth_get_private_keys();
diff --git a/adb/adb_io_test.cpp b/adb/adb_io_test.cpp
index 611b239..91b73a9 100644
--- a/adb/adb_io_test.cpp
+++ b/adb/adb_io_test.cpp
@@ -28,7 +28,6 @@
#include <string>
#include <android-base/file.h>
-#include <android-base/test_utils.h>
// All of these tests fail on Windows because they use the C Runtime open(),
// but the adb_io APIs expect file descriptors from adb_open(). This could
diff --git a/adb/adb_trace.cpp b/adb/adb_trace.cpp
index a8ec5fb..a024a89 100644
--- a/adb/adb_trace.cpp
+++ b/adb/adb_trace.cpp
@@ -41,6 +41,11 @@
const char* tag, const char* file, unsigned int line,
const char* message) {
android::base::StderrLogger(id, severity, tag, file, line, message);
+#if defined(_WIN32)
+ // stderr can be buffered on Windows (and setvbuf doesn't seem to work), so explicitly flush.
+ fflush(stderr);
+#endif
+
#if !ADB_HOST
// Only print logs of INFO or higher to logcat, so that `adb logcat` with adbd tracing on
// doesn't result in exponential logging.
diff --git a/adb/adb_utils_test.cpp b/adb/adb_utils_test.cpp
index 341323f..870f6f0 100644
--- a/adb/adb_utils_test.cpp
+++ b/adb/adb_utils_test.cpp
@@ -30,8 +30,8 @@
#include "sysdeps.h"
+#include <android-base/file.h>
#include <android-base/macros.h>
-#include <android-base/test_utils.h>
#ifdef _WIN32
static std::string subdir(const char* parent, const char* child) {
diff --git a/adb/client/adb_install.cpp b/adb/client/adb_install.cpp
index cf22efa..7f37c45 100644
--- a/adb/client/adb_install.cpp
+++ b/adb/client/adb_install.cpp
@@ -30,7 +30,6 @@
#include "android-base/file.h"
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
-#include "android-base/test_utils.h"
#include "client/file_sync_client.h"
#include "commandline.h"
#include "fastdeploy.h"
@@ -392,7 +391,8 @@
}
if (android::base::EndsWithIgnoreCase(file, ".apk") ||
- android::base::EndsWithIgnoreCase(file, ".dm")) {
+ android::base::EndsWithIgnoreCase(file, ".dm") ||
+ android::base::EndsWithIgnoreCase(file, ".fsv_sig")) {
struct stat sb;
if (stat(file, &sb) != -1) total_size += sb.st_size;
first_apk = i;
diff --git a/adb/client/auth.cpp b/adb/client/auth.cpp
index 71c19b8..bcb829b 100644
--- a/adb/client/auth.cpp
+++ b/adb/client/auth.cpp
@@ -43,6 +43,7 @@
#include "adb.h"
#include "adb_auth.h"
+#include "adb_io.h"
#include "adb_utils.h"
#include "sysdeps.h"
#include "transport.h"
@@ -52,30 +53,7 @@
*new std::map<std::string, std::shared_ptr<RSA>>;
static std::map<int, std::string>& g_monitored_paths = *new std::map<int, std::string>;
-static std::string get_user_info() {
- LOG(INFO) << "get_user_info...";
-
- std::string hostname;
- if (getenv("HOSTNAME")) hostname = getenv("HOSTNAME");
-#if !defined(_WIN32)
- char buf[64];
- if (hostname.empty() && gethostname(buf, sizeof(buf)) != -1) hostname = buf;
-#endif
- if (hostname.empty()) hostname = "unknown";
-
- std::string username;
- if (getenv("LOGNAME")) username = getenv("LOGNAME");
-#if !defined _WIN32 && !defined ADB_HOST_ON_TARGET
- if (username.empty() && getlogin()) username = getlogin();
-#endif
- if (username.empty()) hostname = "unknown";
-
- return " " + username + "@" + hostname;
-}
-
-static bool write_public_keyfile(RSA* private_key, const std::string& private_key_path) {
- LOG(INFO) << "write_public_keyfile...";
-
+static bool calculate_public_key(std::string* out, RSA* private_key) {
uint8_t binary_key_data[ANDROID_PUBKEY_ENCODED_SIZE];
if (!android_pubkey_encode(private_key, binary_key_data, sizeof(binary_key_data))) {
LOG(ERROR) << "Failed to convert to public key";
@@ -88,20 +66,10 @@
return false;
}
- std::string content;
- content.resize(expected_length);
- size_t actual_length = EVP_EncodeBlock(reinterpret_cast<uint8_t*>(&content[0]), binary_key_data,
+ out->resize(expected_length);
+ size_t actual_length = EVP_EncodeBlock(reinterpret_cast<uint8_t*>(out->data()), binary_key_data,
sizeof(binary_key_data));
- content.resize(actual_length);
-
- content += get_user_info();
-
- std::string path(private_key_path + ".pub");
- if (!android::base::WriteStringToFile(content, path)) {
- PLOG(ERROR) << "Failed to write public key to '" << path << "'";
- return false;
- }
-
+ out->resize(actual_length);
return true;
}
@@ -140,11 +108,6 @@
goto out;
}
- if (!write_public_keyfile(rsa, file)) {
- D("Failed to write public key");
- goto out;
- }
-
ret = 1;
out:
@@ -170,36 +133,41 @@
return result;
}
-static bool read_key_file(const std::string& file) {
- LOG(INFO) << "read_key_file '" << file << "'...";
-
+static std::shared_ptr<RSA> read_key_file(const std::string& file) {
std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(file.c_str(), "r"), fclose);
if (!fp) {
PLOG(ERROR) << "Failed to open '" << file << "'";
- return false;
+ return nullptr;
}
RSA* key = RSA_new();
if (!PEM_read_RSAPrivateKey(fp.get(), &key, nullptr, nullptr)) {
LOG(ERROR) << "Failed to read key";
RSA_free(key);
+ return nullptr;
+ }
+
+ return std::shared_ptr<RSA>(key, RSA_free);
+}
+
+static bool load_key(const std::string& file) {
+ std::shared_ptr<RSA> key = read_key_file(file);
+ if (!key) {
return false;
}
std::lock_guard<std::mutex> lock(g_keys_mutex);
- std::string fingerprint = hash_key(key);
+ std::string fingerprint = hash_key(key.get());
if (g_keys.find(fingerprint) != g_keys.end()) {
LOG(INFO) << "ignoring already-loaded key: " << file;
- RSA_free(key);
} else {
- g_keys[fingerprint] = std::shared_ptr<RSA>(key, RSA_free);
+ g_keys[fingerprint] = std::move(key);
}
-
return true;
}
-static bool read_keys(const std::string& path, bool allow_dir = true) {
- LOG(INFO) << "read_keys '" << path << "'...";
+static bool load_keys(const std::string& path, bool allow_dir = true) {
+ LOG(INFO) << "load_keys '" << path << "'...";
struct stat st;
if (stat(path.c_str(), &st) != 0) {
@@ -208,7 +176,7 @@
}
if (S_ISREG(st.st_mode)) {
- return read_key_file(path);
+ return load_key(path);
} else if (S_ISDIR(st.st_mode)) {
if (!allow_dir) {
// inotify isn't recursive. It would break expectations to load keys in nested
@@ -237,7 +205,7 @@
continue;
}
- result |= read_key_file((path + OS_PATH_SEPARATOR + name));
+ result |= load_key((path + OS_PATH_SEPARATOR + name));
}
return result;
}
@@ -250,7 +218,7 @@
return adb_get_android_dir_path() + OS_PATH_SEPARATOR + "adbkey";
}
-static bool get_user_key() {
+static bool generate_userkey() {
std::string path = get_user_key_path();
if (path.empty()) {
PLOG(ERROR) << "Error getting user key filename";
@@ -266,7 +234,7 @@
}
}
- return read_key_file(path);
+ return load_key(path);
}
static std::set<std::string> get_vendor_keys() {
@@ -320,26 +288,42 @@
return result;
}
+static bool pubkey_from_privkey(std::string* out, const std::string& path) {
+ std::shared_ptr<RSA> privkey = read_key_file(path);
+ if (!privkey) {
+ return false;
+ }
+ return calculate_public_key(out, privkey.get());
+}
+
std::string adb_auth_get_userkey() {
std::string path = get_user_key_path();
if (path.empty()) {
PLOG(ERROR) << "Error getting user key filename";
return "";
}
- path += ".pub";
- std::string content;
- if (!android::base::ReadFileToString(path, &content)) {
- PLOG(ERROR) << "Can't load '" << path << "'";
+ std::string result;
+ if (!pubkey_from_privkey(&result, path)) {
return "";
}
- return content;
+ return result;
}
int adb_auth_keygen(const char* filename) {
return (generate_key(filename) == 0);
}
+int adb_auth_pubkey(const char* filename) {
+ std::string pubkey;
+ if (!pubkey_from_privkey(&pubkey, filename)) {
+ return 1;
+ }
+ pubkey.push_back('\n');
+
+ return WriteFdExactly(STDOUT_FILENO, pubkey.data(), pubkey.size()) ? 0 : 1;
+}
+
#if defined(__linux__)
static void adb_auth_inotify_update(int fd, unsigned fd_event, void*) {
LOG(INFO) << "adb_auth_inotify_update called";
@@ -380,7 +364,7 @@
LOG(INFO) << "ignoring new directory at '" << path << "'";
} else {
LOG(INFO) << "observed new file at '" << path << "'";
- read_keys(path, false);
+ load_keys(path, false);
}
} else {
LOG(WARNING) << "unmonitored event for " << path << ": 0x" << std::hex
@@ -420,8 +404,8 @@
void adb_auth_init() {
LOG(INFO) << "adb_auth_init...";
- if (!get_user_key()) {
- LOG(ERROR) << "Failed to get user key";
+ if (!generate_userkey()) {
+ LOG(ERROR) << "Failed to generate user key";
return;
}
@@ -432,7 +416,7 @@
#endif
for (const std::string& path : key_paths) {
- read_keys(path.c_str());
+ load_keys(path.c_str());
}
}
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index b5bed28..c11052d 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -185,7 +185,6 @@
" enable-verity re-enable dm-verity checking on userdebug builds\n"
" keygen FILE\n"
" generate adb public/private key; private key stored in FILE,\n"
- " public key stored in FILE.pub (existing files overwritten)\n"
"\n"
"scripting:\n"
" wait-for[-TRANSPORT]-STATE\n"
@@ -1756,14 +1755,14 @@
// Always print key generation information for keygen command.
adb_trace_enable(AUTH);
return adb_auth_keygen(argv[1]);
- }
- else if (!strcmp(argv[0], "jdwp")) {
+ } else if (!strcmp(argv[0], "pubkey")) {
+ if (argc != 2) error_exit("pubkey requires an argument");
+ return adb_auth_pubkey(argv[1]);
+ } else if (!strcmp(argv[0], "jdwp")) {
return adb_connect_command("jdwp");
- }
- else if (!strcmp(argv[0], "track-jdwp")) {
+ } else if (!strcmp(argv[0], "track-jdwp")) {
return adb_connect_command("track-jdwp");
- }
- else if (!strcmp(argv[0], "track-devices")) {
+ } else if (!strcmp(argv[0], "track-devices")) {
return adb_connect_command("host:track-devices");
} else if (!strcmp(argv[0], "raw")) {
if (argc != 2) {
@@ -1772,13 +1771,11 @@
return adb_connect_command(argv[1]);
}
-
/* "adb /?" is a common idiom under Windows */
else if (!strcmp(argv[0], "--help") || !strcmp(argv[0], "help") || !strcmp(argv[0], "/?")) {
help();
return 0;
- }
- else if (!strcmp(argv[0], "--version") || !strcmp(argv[0], "version")) {
+ } else if (!strcmp(argv[0], "--version") || !strcmp(argv[0], "version")) {
fprintf(stdout, "%s", adb_version().c_str());
return 0;
} else if (!strcmp(argv[0], "features")) {
diff --git a/adb/client/console.cpp b/adb/client/console.cpp
index 9563eac..4e8a3f8 100644
--- a/adb/client/console.cpp
+++ b/adb/client/console.cpp
@@ -135,6 +135,7 @@
// preventing the emulator from reading the command that adb has sent.
// https://code.google.com/p/android/issues/detail?id=21021
int result;
+ std::string emulator_output;
do {
char buf[BUFSIZ];
result = adb_read(fd, buf, sizeof(buf));
@@ -146,8 +147,37 @@
// appended above, and that causes the emulator to close the socket
// which should cause zero bytes (orderly/graceful shutdown) to be
// returned.
+ if (result > 0) emulator_output.append(buf, result);
} while (result > 0);
+ // Note: the following messages are expected to be quite stable from emulator.
+ //
+ // Emulator console will send the following message upon connection:
+ //
+ // Android Console: Authentication required
+ // Android Console: type 'auth <auth_token>' to authenticate
+ // Android Console: you can find your <auth_token> in
+ // '/<path-to-home>/.emulator_console_auth_token'
+ // OK\r\n
+ //
+ // and the following after authentication:
+ // Android Console: type 'help' for a list of commands
+ // OK\r\n
+ //
+ // So try search and skip first two "OK\r\n", print the rest.
+ //
+ const std::string delims = "OK\r\n";
+ size_t found = 0;
+ for (int i = 0; i < 2; ++i) {
+ const size_t result = emulator_output.find(delims, found);
+ if (result == std::string::npos) {
+ break;
+ } else {
+ found = result + delims.size();
+ }
+ }
+
+ printf("%s", emulator_output.c_str() + found);
adb_close(fd);
return 0;
diff --git a/adb/client/usb_libusb.cpp b/adb/client/usb_libusb.cpp
index 10b6090..f2ca63b 100644
--- a/adb/client/usb_libusb.cpp
+++ b/adb/client/usb_libusb.cpp
@@ -332,13 +332,6 @@
return;
}
- rc = libusb_set_interface_alt_setting(handle.get(), interface_num, 0);
- if (rc != 0) {
- LOG(WARNING) << "failed to set interface alt setting for device '" << device_serial
- << "'" << libusb_error_name(rc);
- return;
- }
-
for (uint8_t endpoint : {bulk_in, bulk_out}) {
rc = libusb_clear_halt(handle.get(), endpoint);
if (rc != 0) {
diff --git a/adb/client/usb_osx.cpp b/adb/client/usb_osx.cpp
index 49baf36..e380c84 100644
--- a/adb/client/usb_osx.cpp
+++ b/adb/client/usb_osx.cpp
@@ -136,8 +136,8 @@
io_service_t usbDevice;
io_service_t usbInterface;
IOCFPlugInInterface **plugInInterface = NULL;
- IOUSBInterfaceInterface220 **iface = NULL;
- IOUSBDeviceInterface197 **dev = NULL;
+ IOUSBInterfaceInterface500 **iface = NULL;
+ IOUSBDeviceInterface500 **dev = NULL;
HRESULT result;
SInt32 score;
uint32_t locationId;
@@ -163,7 +163,7 @@
//* This gets us the interface object
result = (*plugInInterface)->QueryInterface(
plugInInterface,
- CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID), (LPVOID*)&iface);
+ CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID500), (LPVOID*)&iface);
//* We only needed the plugin to get the interface, so discard it
(*plugInInterface)->Release(plugInInterface);
if (result || !iface) {
@@ -209,7 +209,7 @@
}
result = (*plugInInterface)->QueryInterface(plugInInterface,
- CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID*)&dev);
+ CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID500), (LPVOID*)&dev);
//* only needed this to query the plugin
(*plugInInterface)->Release(plugInInterface);
if (result || !dev) {
diff --git a/adb/daemon/remount_service.cpp b/adb/daemon/remount_service.cpp
index ae02525..b72ed16 100644
--- a/adb/daemon/remount_service.cpp
+++ b/adb/daemon/remount_service.cpp
@@ -78,7 +78,13 @@
std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
fs_mgr_free_fstab);
struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab.get(), dir);
- return rec ? rec->blk_device : "";
+ if (!rec) {
+ return "";
+ }
+ if (fs_mgr_is_logical(rec)) {
+ fs_mgr_update_logical_partition(rec);
+ }
+ return rec->blk_device;
}
// The proc entry for / is full of lies, so check fstab instead.
@@ -87,12 +93,16 @@
if (is_root) {
return find_fstab_mount(dir);
} else {
- return find_proc_mount(dir);
+ return find_proc_mount(dir);
}
}
+bool dev_is_overlayfs(const std::string& dev) {
+ return (dev == "overlay") || (dev == "overlayfs");
+}
+
bool make_block_device_writable(const std::string& dev) {
- if ((dev == "overlay") || (dev == "overlayfs")) return true;
+ if (dev_is_overlayfs(dev)) return true;
int fd = unix_open(dev.c_str(), O_RDONLY | O_CLOEXEC);
if (fd == -1) {
return false;
@@ -155,11 +165,16 @@
return true;
}
bool is_root = strcmp(dir, "/") == 0;
- if (is_root && !find_mount("/system", false).empty()) {
+ if (is_root && dev_is_overlayfs(find_mount("/system", false))) {
dir = "/system";
is_root = false;
}
std::string dev = find_mount(dir, is_root);
+ if (is_root && dev.empty()) {
+ // The fstab entry will be /system if the device switched roots during
+ // first-stage init.
+ dev = find_mount("/system", true);
+ }
// Even if the device for the root is not found, we still try to remount it
// as rw. This typically only happens when running Android in a container:
// the root will almost always be in a loop device, which is dynamic, so
diff --git a/adb/daemon/services.cpp b/adb/daemon/services.cpp
index 720ec6a..b300fac 100644
--- a/adb/daemon/services.cpp
+++ b/adb/daemon/services.cpp
@@ -104,7 +104,7 @@
std::string reboot_string = android::base::StringPrintf("reboot,%s", reboot_arg.c_str());
if (reboot_arg == "fastboot" &&
- android::base::GetBoolProperty("ro.boot.logical_partitions", false) &&
+ android::base::GetBoolProperty("ro.boot.dynamic_partitions", false) &&
access("/dev/socket/recovery", F_OK) == 0) {
LOG(INFO) << "Recovery specific reboot fastboot";
/*
diff --git a/adb/sysdeps/stat_test.cpp b/adb/sysdeps/stat_test.cpp
index 2c2e0ee..67155d9 100644
--- a/adb/sysdeps/stat_test.cpp
+++ b/adb/sysdeps/stat_test.cpp
@@ -16,7 +16,7 @@
#include <string>
-#include <android-base/test_utils.h>
+#include <android-base/file.h>
#include <gtest/gtest.h>
#include "adb_utils.h"
diff --git a/adb/sysdeps_win32_test.cpp b/adb/sysdeps_win32_test.cpp
index 529b212..183cd5b 100644
--- a/adb/sysdeps_win32_test.cpp
+++ b/adb/sysdeps_win32_test.cpp
@@ -18,7 +18,7 @@
#include "sysdeps.h"
-#include <android-base/test_utils.h>
+#include <android-base/file.h>
TEST(sysdeps_win32, adb_getenv) {
// Insert all test env vars before first call to adb_getenv() which will
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 03a9f30..c2d4917 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -1013,9 +1013,10 @@
#if ADB_HOST
kFeatureApex
#endif
- // Increment ADB_SERVER_VERSION whenever the feature list changes to
- // make sure that the adb client and server features stay in sync
- // (http://b/24370690).
+ // Increment ADB_SERVER_VERSION when adding a feature that adbd needs
+ // to know about. Otherwise, the client can be stuck running an old
+ // version of the server even after upgrading their copy of adb.
+ // (http://b/24370690)
};
return *features;
diff --git a/adb/transport_fd.cpp b/adb/transport_fd.cpp
index ec61279..a93e68a 100644
--- a/adb/transport_fd.cpp
+++ b/adb/transport_fd.cpp
@@ -85,18 +85,9 @@
if (pfds[0].revents) {
if ((pfds[0].revents & POLLOUT)) {
std::lock_guard<std::mutex> lock(this->write_mutex_);
- WriteResult result = DispatchWrites();
- switch (result) {
- case WriteResult::Error:
- *error = "write failed";
- return;
-
- case WriteResult::Completed:
- writable_ = true;
- break;
-
- case WriteResult::TryAgain:
- break;
+ if (DispatchWrites() == WriteResult::Error) {
+ *error = "write failed";
+ return;
}
}
@@ -179,13 +170,14 @@
WriteResult DispatchWrites() REQUIRES(write_mutex_) {
CHECK(!write_buffer_.empty());
- if (!writable_) {
- return WriteResult::TryAgain;
- }
-
auto iovs = write_buffer_.iovecs();
ssize_t rc = adb_writev(fd_.get(), iovs.data(), iovs.size());
if (rc == -1) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ writable_ = false;
+ return WriteResult::TryAgain;
+ }
+
return WriteResult::Error;
} else if (rc == 0) {
errno = 0;
@@ -194,6 +186,7 @@
// TODO: Implement a more efficient drop_front?
write_buffer_.take_front(rc);
+ writable_ = write_buffer_.empty();
if (write_buffer_.empty()) {
return WriteResult::Completed;
}
@@ -211,7 +204,12 @@
if (!packet->payload.empty()) {
write_buffer_.append(std::make_unique<IOVector::block_type>(std::move(packet->payload)));
}
- return DispatchWrites() != WriteResult::Error;
+
+ WriteResult result = DispatchWrites();
+ if (result == WriteResult::TryAgain) {
+ WakeThread();
+ }
+ return result != WriteResult::Error;
}
std::thread thread_;
diff --git a/adb/types.h b/adb/types.h
index 0c71c3a..0090c98 100644
--- a/adb/types.h
+++ b/adb/types.h
@@ -108,7 +108,10 @@
CHECK_EQ(0ULL, capacity_);
CHECK_EQ(0ULL, size_);
if (size != 0) {
- data_ = std::make_unique<char[]>(size);
+ // This isn't std::make_unique because that's equivalent to `new char[size]()`, which
+ // value-initializes the array instead of leaving it uninitialized. As an optimization,
+ // call new without parentheses to avoid this costly initialization.
+ data_.reset(new char[size]);
capacity_ = size;
size_ = size;
}
diff --git a/base/file.cpp b/base/file.cpp
index 3834ed4..d5bb7fe 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -18,7 +18,10 @@
#include <errno.h>
#include <fcntl.h>
+#include <ftw.h>
#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
@@ -28,19 +31,144 @@
#include <string>
#include <vector>
-#include "android-base/logging.h"
-#include "android-base/macros.h" // For TEMP_FAILURE_RETRY on Darwin.
-#include "android-base/unique_fd.h"
-#include "android-base/utf8.h"
-
#if defined(__APPLE__)
#include <mach-o/dyld.h>
#endif
#if defined(_WIN32)
+#include <direct.h>
#include <windows.h>
#define O_NOFOLLOW 0
+#define OS_PATH_SEPARATOR '\\'
+#else
+#define OS_PATH_SEPARATOR '/'
#endif
+#include "android-base/logging.h" // and must be after windows.h for ERROR
+#include "android-base/macros.h" // For TEMP_FAILURE_RETRY on Darwin.
+#include "android-base/unique_fd.h"
+#include "android-base/utf8.h"
+
+#ifdef _WIN32
+int mkstemp(char* template_name) {
+ if (_mktemp(template_name) == nullptr) {
+ return -1;
+ }
+ // Use open() to match the close() that TemporaryFile's destructor does.
+ // Use O_BINARY to match base file APIs.
+ return open(template_name, O_CREAT | O_EXCL | O_RDWR | O_BINARY, S_IRUSR | S_IWUSR);
+}
+
+char* mkdtemp(char* template_name) {
+ if (_mktemp(template_name) == nullptr) {
+ return nullptr;
+ }
+ if (_mkdir(template_name) == -1) {
+ return nullptr;
+ }
+ return template_name;
+}
+#endif
+
+namespace {
+
+std::string GetSystemTempDir() {
+#if defined(__ANDROID__)
+ const auto* tmpdir = getenv("TMPDIR");
+ if (tmpdir == nullptr) tmpdir = "/data/local/tmp";
+ if (access(tmpdir, R_OK | W_OK | X_OK) == 0) {
+ return tmpdir;
+ }
+ // Tests running in app context can't access /data/local/tmp,
+ // so try current directory if /data/local/tmp is not accessible.
+ return ".";
+#elif defined(_WIN32)
+ char tmp_dir[MAX_PATH];
+ DWORD result = GetTempPathA(sizeof(tmp_dir), tmp_dir); // checks TMP env
+ CHECK_NE(result, 0ul) << "GetTempPathA failed, error: " << GetLastError();
+ CHECK_LT(result, sizeof(tmp_dir)) << "path truncated to: " << result;
+
+ // GetTempPath() returns a path with a trailing slash, but init()
+ // does not expect that, so remove it.
+ CHECK_EQ(tmp_dir[result - 1], '\\');
+ tmp_dir[result - 1] = '\0';
+ return tmp_dir;
+#else
+ const auto* tmpdir = getenv("TMPDIR");
+ if (tmpdir == nullptr) tmpdir = "/tmp";
+ return tmpdir;
+#endif
+}
+
+} // namespace
+
+TemporaryFile::TemporaryFile() {
+ init(GetSystemTempDir());
+}
+
+TemporaryFile::TemporaryFile(const std::string& tmp_dir) {
+ init(tmp_dir);
+}
+
+TemporaryFile::~TemporaryFile() {
+ if (fd != -1) {
+ close(fd);
+ }
+ if (remove_file_) {
+ unlink(path);
+ }
+}
+
+int TemporaryFile::release() {
+ int result = fd;
+ fd = -1;
+ return result;
+}
+
+void TemporaryFile::init(const std::string& tmp_dir) {
+ snprintf(path, sizeof(path), "%s%cTemporaryFile-XXXXXX", tmp_dir.c_str(), OS_PATH_SEPARATOR);
+ fd = mkstemp(path);
+}
+
+TemporaryDir::TemporaryDir() {
+ init(GetSystemTempDir());
+}
+
+TemporaryDir::~TemporaryDir() {
+ if (!remove_dir_and_contents_) return;
+
+ auto callback = [](const char* child, const struct stat*, int file_type, struct FTW*) -> int {
+ switch (file_type) {
+ case FTW_D:
+ case FTW_DP:
+ case FTW_DNR:
+ if (rmdir(child) == -1) {
+ PLOG(ERROR) << "rmdir " << child;
+ }
+ break;
+ case FTW_NS:
+ default:
+ if (rmdir(child) != -1) break;
+ // FALLTHRU (for gcc, lint, pcc, etc; and following for clang)
+ FALLTHROUGH_INTENDED;
+ case FTW_F:
+ case FTW_SL:
+ case FTW_SLN:
+ if (unlink(child) == -1) {
+ PLOG(ERROR) << "unlink " << child;
+ }
+ break;
+ }
+ return 0;
+ };
+
+ nftw(path, callback, 128, FTW_DEPTH | FTW_MOUNT | FTW_PHYS);
+}
+
+bool TemporaryDir::init(const std::string& tmp_dir) {
+ snprintf(path, sizeof(path), "%s%cTemporaryDir-XXXXXX", tmp_dir.c_str(), OS_PATH_SEPARATOR);
+ return (mkdtemp(path) != nullptr);
+}
+
namespace android {
namespace base {
diff --git a/base/file_test.cpp b/base/file_test.cpp
index 6794652..f64e81c 100644
--- a/base/file_test.cpp
+++ b/base/file_test.cpp
@@ -24,8 +24,6 @@
#include <string>
-#include "android-base/test_utils.h"
-
#if !defined(_WIN32)
#include <pwd.h>
#endif
diff --git a/base/include/android-base/file.h b/base/include/android-base/file.h
index 86d537d..f8748b5 100644
--- a/base/include/android-base/file.h
+++ b/base/include/android-base/file.h
@@ -18,8 +18,10 @@
#include <sys/stat.h>
#include <sys/types.h>
+
#include <string>
+#include <android-base/macros.h>
#include "android-base/off64_t.h"
#if !defined(_WIN32) && !defined(O_BINARY)
@@ -32,6 +34,46 @@
#define O_CLOEXEC O_NOINHERIT
#endif
+class TemporaryFile {
+ public:
+ TemporaryFile();
+ explicit TemporaryFile(const std::string& tmp_dir);
+ ~TemporaryFile();
+
+ // Release the ownership of fd, caller is reponsible for closing the
+ // fd or stream properly.
+ int release();
+ // Don't remove the temporary file in the destructor.
+ void DoNotRemove() { remove_file_ = false; }
+
+ int fd;
+ char path[1024];
+
+ private:
+ void init(const std::string& tmp_dir);
+
+ bool remove_file_ = true;
+
+ DISALLOW_COPY_AND_ASSIGN(TemporaryFile);
+};
+
+class TemporaryDir {
+ public:
+ TemporaryDir();
+ ~TemporaryDir();
+ // Don't remove the temporary dir in the destructor.
+ void DoNotRemove() { remove_dir_and_contents_ = false; }
+
+ char path[1024];
+
+ private:
+ bool init(const std::string& tmp_dir);
+
+ bool remove_dir_and_contents_ = true;
+
+ DISALLOW_COPY_AND_ASSIGN(TemporaryDir);
+};
+
namespace android {
namespace base {
diff --git a/base/include/android-base/strings.h b/base/include/android-base/strings.h
index 9c35560..fc5c1ce 100644
--- a/base/include/android-base/strings.h
+++ b/base/include/android-base/strings.h
@@ -61,6 +61,7 @@
bool StartsWithIgnoreCase(const std::string& s, const char* prefix);
bool StartsWith(const std::string& s, const std::string& prefix);
bool StartsWithIgnoreCase(const std::string& s, const std::string& prefix);
+bool StartsWith(const std::string& s, char prefix);
// Tests whether 's' ends with 'suffix'.
// TODO: string_view
@@ -68,6 +69,7 @@
bool EndsWithIgnoreCase(const std::string& s, const char* suffix);
bool EndsWith(const std::string& s, const std::string& suffix);
bool EndsWithIgnoreCase(const std::string& s, const std::string& suffix);
+bool EndsWith(const std::string& s, char suffix);
// Tests whether 'lhs' equals 'rhs', ignoring case.
bool EqualsIgnoreCase(const std::string& lhs, const std::string& rhs);
diff --git a/base/include/android-base/test_utils.h b/base/include/android-base/test_utils.h
index 2abe68e..b20f278 100644
--- a/base/include/android-base/test_utils.h
+++ b/base/include/android-base/test_utils.h
@@ -19,44 +19,9 @@
#include <regex>
#include <string>
+#include <android-base/file.h>
#include <android-base/macros.h>
-class TemporaryFile {
- public:
- TemporaryFile();
- explicit TemporaryFile(const std::string& tmp_dir);
- ~TemporaryFile();
-
- // Release the ownership of fd, caller is reponsible for closing the
- // fd or stream properly.
- int release();
- // Don't remove the temporary file in the destructor.
- void DoNotRemove() { remove_file_ = false; }
-
- int fd;
- char path[1024];
-
- private:
- void init(const std::string& tmp_dir);
-
- bool remove_file_ = true;
-
- DISALLOW_COPY_AND_ASSIGN(TemporaryFile);
-};
-
-class TemporaryDir {
- public:
- TemporaryDir();
- ~TemporaryDir();
-
- char path[1024];
-
- private:
- bool init(const std::string& tmp_dir);
-
- DISALLOW_COPY_AND_ASSIGN(TemporaryDir);
-};
-
class CapturedStdFd {
public:
CapturedStdFd(int std_fd);
diff --git a/base/include/android-base/unique_fd.h b/base/include/android-base/unique_fd.h
index cd2dc04..4e3879b 100644
--- a/base/include/android-base/unique_fd.h
+++ b/base/include/android-base/unique_fd.h
@@ -19,6 +19,7 @@
#include <fcntl.h>
#if !defined(_WIN32)
+#include <dirent.h>
#include <sys/socket.h>
#endif
@@ -211,6 +212,17 @@
return file;
}
+// Using fdopendir with unique_fd correctly is more annoying than it should be,
+// because fdopen doesn't close the file descriptor received upon failure.
+inline DIR* Fdopendir(unique_fd&& ufd) {
+ int fd = ufd.release();
+ DIR* dir = fdopendir(fd);
+ if (dir == nullptr) {
+ close(fd);
+ }
+ return dir;
+}
+
#endif // !defined(_WIN32)
} // namespace base
diff --git a/base/mapped_file_test.cpp b/base/mapped_file_test.cpp
index 57fde6f..7e89723 100644
--- a/base/mapped_file_test.cpp
+++ b/base/mapped_file_test.cpp
@@ -25,7 +25,6 @@
#include <string>
#include "android-base/file.h"
-#include "android-base/test_utils.h"
#include "android-base/unique_fd.h"
TEST(mapped_file, smoke) {
diff --git a/base/strings.cpp b/base/strings.cpp
index a8bb2a9..2d6eef0 100644
--- a/base/strings.cpp
+++ b/base/strings.cpp
@@ -95,6 +95,10 @@
return strncmp(s.c_str(), prefix.c_str(), prefix.size()) == 0;
}
+bool StartsWith(const std::string& s, char prefix) {
+ return *s.c_str() == prefix; // Use c_str() to guarantee there is at least a '\0'.
+}
+
bool StartsWithIgnoreCase(const std::string& s, const char* prefix) {
return strncasecmp(s.c_str(), prefix, strlen(prefix)) == 0;
}
@@ -121,6 +125,10 @@
return EndsWith(s, suffix.c_str(), suffix.size(), true);
}
+bool EndsWith(const std::string& s, char suffix) {
+ return EndsWith(s, &suffix, 1, true);
+}
+
bool EndsWithIgnoreCase(const std::string& s, const char* suffix) {
return EndsWith(s, suffix, strlen(suffix), false);
}
diff --git a/base/strings_test.cpp b/base/strings_test.cpp
index b8639ea..9d74094 100644
--- a/base/strings_test.cpp
+++ b/base/strings_test.cpp
@@ -198,6 +198,12 @@
ASSERT_FALSE(android::base::StartsWithIgnoreCase("foobar", "BAR"));
}
+TEST(strings, StartsWith_char) {
+ ASSERT_FALSE(android::base::StartsWith("", 'f'));
+ ASSERT_TRUE(android::base::StartsWith("foo", 'f'));
+ ASSERT_FALSE(android::base::StartsWith("foo", 'o'));
+}
+
TEST(strings, EndsWith_empty) {
ASSERT_FALSE(android::base::EndsWith("", "foo"));
ASSERT_TRUE(android::base::EndsWith("", ""));
@@ -273,6 +279,12 @@
ASSERT_FALSE(android::base::EndsWithIgnoreCase("GoOdByE", std::string{"lo"}));
}
+TEST(strings, EndsWith_char) {
+ ASSERT_FALSE(android::base::EndsWith("", 'o'));
+ ASSERT_TRUE(android::base::EndsWith("foo", 'o'));
+ ASSERT_FALSE(android::base::EndsWith("foo", "f"));
+}
+
TEST(strings, EqualsIgnoreCase) {
ASSERT_TRUE(android::base::EqualsIgnoreCase("foo", "FOO"));
ASSERT_TRUE(android::base::EqualsIgnoreCase("FOO", "foo"));
diff --git a/base/test_utils.cpp b/base/test_utils.cpp
index 4d9466b..36b4cdf 100644
--- a/base/test_utils.cpp
+++ b/base/test_utils.cpp
@@ -22,109 +22,11 @@
#include <sys/stat.h>
#include <unistd.h>
-#if defined(_WIN32)
-#include <windows.h>
-#include <direct.h>
-#define OS_PATH_SEPARATOR '\\'
-#else
-#define OS_PATH_SEPARATOR '/'
-#endif
-
#include <string>
#include <android-base/file.h>
#include <android-base/logging.h>
-#ifdef _WIN32
-int mkstemp(char* template_name) {
- if (_mktemp(template_name) == nullptr) {
- return -1;
- }
- // Use open() to match the close() that TemporaryFile's destructor does.
- // Use O_BINARY to match base file APIs.
- return open(template_name, O_CREAT | O_EXCL | O_RDWR | O_BINARY,
- S_IRUSR | S_IWUSR);
-}
-
-char* mkdtemp(char* template_name) {
- if (_mktemp(template_name) == nullptr) {
- return nullptr;
- }
- if (_mkdir(template_name) == -1) {
- return nullptr;
- }
- return template_name;
-}
-#endif
-
-static std::string GetSystemTempDir() {
-#if defined(__ANDROID__)
- const char* tmpdir = "/data/local/tmp";
- if (access(tmpdir, R_OK | W_OK | X_OK) == 0) {
- return tmpdir;
- }
- // Tests running in app context can't access /data/local/tmp,
- // so try current directory if /data/local/tmp is not accessible.
- return ".";
-#elif defined(_WIN32)
- char tmp_dir[MAX_PATH];
- DWORD result = GetTempPathA(sizeof(tmp_dir), tmp_dir);
- CHECK_NE(result, 0ul) << "GetTempPathA failed, error: " << GetLastError();
- CHECK_LT(result, sizeof(tmp_dir)) << "path truncated to: " << result;
-
- // GetTempPath() returns a path with a trailing slash, but init()
- // does not expect that, so remove it.
- CHECK_EQ(tmp_dir[result - 1], '\\');
- tmp_dir[result - 1] = '\0';
- return tmp_dir;
-#else
- return "/tmp";
-#endif
-}
-
-TemporaryFile::TemporaryFile() {
- init(GetSystemTempDir());
-}
-
-TemporaryFile::TemporaryFile(const std::string& tmp_dir) {
- init(tmp_dir);
-}
-
-TemporaryFile::~TemporaryFile() {
- if (fd != -1) {
- close(fd);
- }
- if (remove_file_) {
- unlink(path);
- }
-}
-
-int TemporaryFile::release() {
- int result = fd;
- fd = -1;
- return result;
-}
-
-void TemporaryFile::init(const std::string& tmp_dir) {
- snprintf(path, sizeof(path), "%s%cTemporaryFile-XXXXXX", tmp_dir.c_str(),
- OS_PATH_SEPARATOR);
- fd = mkstemp(path);
-}
-
-TemporaryDir::TemporaryDir() {
- init(GetSystemTempDir());
-}
-
-TemporaryDir::~TemporaryDir() {
- rmdir(path);
-}
-
-bool TemporaryDir::init(const std::string& tmp_dir) {
- snprintf(path, sizeof(path), "%s%cTemporaryDir-XXXXXX", tmp_dir.c_str(),
- OS_PATH_SEPARATOR);
- return (mkdtemp(path) != nullptr);
-}
-
CapturedStdFd::CapturedStdFd(int std_fd) : std_fd_(std_fd), old_fd_(-1) {
Start();
}
diff --git a/base/utf8_test.cpp b/base/utf8_test.cpp
index fcb25c3..472e82c 100644
--- a/base/utf8_test.cpp
+++ b/base/utf8_test.cpp
@@ -21,8 +21,8 @@
#include <fcntl.h>
#include <stdlib.h>
+#include "android-base/file.h"
#include "android-base/macros.h"
-#include "android-base/test_utils.h"
#include "android-base/unique_fd.h"
namespace android {
diff --git a/bootstat/boot_event_record_store_test.cpp b/bootstat/boot_event_record_store_test.cpp
index 4b7ab36..5ca9b09 100644
--- a/bootstat/boot_event_record_store_test.cpp
+++ b/bootstat/boot_event_record_store_test.cpp
@@ -29,7 +29,6 @@
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
-#include <android-base/test_utils.h>
#include <android-base/unique_fd.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
diff --git a/cpio/Android.bp b/cpio/Android.bp
new file mode 100644
index 0000000..baa0319
--- /dev/null
+++ b/cpio/Android.bp
@@ -0,0 +1,11 @@
+// Copyright 2005 The Android Open Source Project
+
+cc_binary_host {
+ name: "mkbootfs",
+ srcs: ["mkbootfs.c"],
+ cflags: ["-Werror"],
+ shared_libs: ["libcutils"],
+ dist: {
+ targets: ["dist_files"],
+ },
+}
diff --git a/cpio/Android.mk b/cpio/Android.mk
deleted file mode 100644
index 2aa7297..0000000
--- a/cpio/Android.mk
+++ /dev/null
@@ -1,17 +0,0 @@
-# Copyright 2005 The Android Open Source Project
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
- mkbootfs.c
-
-LOCAL_MODULE := mkbootfs
-
-LOCAL_CFLAGS := -Werror
-
-LOCAL_SHARED_LIBRARIES := libcutils
-
-include $(BUILD_HOST_EXECUTABLE)
-
-$(call dist-for-goals,dist_files,$(LOCAL_BUILT_MODULE))
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index ed7423b..15c0265 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -250,11 +250,12 @@
}
uint64_t expected = pack_thread_fd(-1, -1);
- if (!trace_output.compare_exchange_strong(expected,
- pack_thread_fd(tid, pipe_write.release()))) {
+ int sent_fd = pipe_write.release();
+ if (!trace_output.compare_exchange_strong(expected, pack_thread_fd(tid, sent_fd))) {
auto [tid, fd] = unpack_thread_fd(expected);
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
"thread %d is already outputting to fd %d?", tid, fd);
+ close(sent_fd);
return false;
}
diff --git a/debuggerd/libdebuggerd/test/open_files_list_test.cpp b/debuggerd/libdebuggerd/test/open_files_list_test.cpp
index d7036fd..3e920eb 100644
--- a/debuggerd/libdebuggerd/test/open_files_list_test.cpp
+++ b/debuggerd/libdebuggerd/test/open_files_list_test.cpp
@@ -20,10 +20,9 @@
#include <string>
+#include <android-base/file.h>
#include <gtest/gtest.h>
-#include "android-base/test_utils.h"
-
#include "libdebuggerd/open_files_list.h"
// Check that we can produce a list of open files for the current process, and
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 0b8a936..1179263 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -78,7 +78,7 @@
_LOG(log, logtype::HEADER, "ABI: '%s'\n", ABI_STRING);
}
-static void dump_probable_cause(log_t* log, const siginfo_t* si) {
+static void dump_probable_cause(log_t* log, const siginfo_t* si, BacktraceMap* map) {
std::string cause;
if (si->si_signo == SIGSEGV && si->si_code == SEGV_MAPERR) {
if (si->si_addr < reinterpret_cast<void*>(4096)) {
@@ -94,6 +94,14 @@
} else if (si->si_addr == reinterpret_cast<void*>(0xffff0f60)) {
cause = "call to kuser_cmpxchg64";
}
+ } else if (si->si_signo == SIGSEGV && si->si_code == SEGV_ACCERR) {
+ for (auto it = map->begin(); it != map->end(); ++it) {
+ const backtrace_map_t* entry = *it;
+ if (si->si_addr >= reinterpret_cast<void*>(entry->start) &&
+ si->si_addr < reinterpret_cast<void*>(entry->end) && entry->flags == PROT_EXEC) {
+ cause = "execute-only (no-read) memory access error; likely due to data in .text.";
+ }
+ }
} else if (si->si_signo == SIGSYS && si->si_code == SYS_SECCOMP) {
cause = StringPrintf("seccomp prevented call to disallowed %s system call %d", ABI_STRING,
si->si_syscall);
@@ -125,8 +133,6 @@
_LOG(log, logtype::HEADER, "signal %d (%s), code %d (%s%s), fault addr %s\n",
thread_info.siginfo->si_signo, get_signame(thread_info.siginfo),
thread_info.siginfo->si_code, get_sigcode(thread_info.siginfo), sender_desc, addr_desc);
-
- dump_probable_cause(log, thread_info.siginfo);
}
static void dump_thread_info(log_t* log, const ThreadInfo& thread_info) {
@@ -426,6 +432,7 @@
if (thread_info.siginfo) {
dump_signal_info(log, thread_info, process_memory);
+ dump_probable_cause(log, thread_info.siginfo, map);
}
if (primary_thread) {
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index 8bdc02f..d0c5234 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -382,6 +382,8 @@
case TRAP_TRACE: return "TRAP_TRACE";
case TRAP_BRANCH: return "TRAP_BRANCH";
case TRAP_HWBKPT: return "TRAP_HWBKPT";
+ case TRAP_UNK:
+ return "TRAP_UNDIAGNOSED";
}
if ((si->si_code & 0xff) == SIGTRAP) {
switch ((si->si_code >> 8) & 0xff) {
@@ -403,7 +405,7 @@
return "PTRACE_EVENT_STOP";
}
}
- static_assert(NSIGTRAP == TRAP_HWBKPT, "missing TRAP_* si_code");
+ static_assert(NSIGTRAP == TRAP_UNK, "missing TRAP_* si_code");
break;
}
// Then the other codes...
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 50d18ed..8006c41 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -43,6 +43,7 @@
"libgtest_main",
"libbase",
"libadb_host",
+ "liblp",
],
header_libs: [
@@ -173,6 +174,11 @@
host_ldlibs: ["-lws2_32"],
},
+ not_windows: {
+ static_libs: [
+ "libext4_utils",
+ ],
+ },
},
stl: "libc++_static",
@@ -193,6 +199,8 @@
"libbase",
"libcutils",
"libgtest_host",
+ "liblp",
+ "libcrypto",
],
}
@@ -252,6 +260,13 @@
"mke2fs",
"make_f2fs",
],
+ dist: {
+ targets: [
+ "dist_files",
+ "sdk",
+ "win_sdk",
+ ],
+ },
target: {
not_windows: {
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index e4c1317..17ec392 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -18,13 +18,9 @@
# Package fastboot-related executables.
#
-my_dist_files := $(HOST_OUT_EXECUTABLES)/fastboot
-my_dist_files += $(HOST_OUT_EXECUTABLES)/mke2fs
+my_dist_files := $(HOST_OUT_EXECUTABLES)/mke2fs
my_dist_files += $(HOST_OUT_EXECUTABLES)/e2fsdroid
my_dist_files += $(HOST_OUT_EXECUTABLES)/make_f2fs
my_dist_files += $(HOST_OUT_EXECUTABLES)/sload_f2fs
$(call dist-for-goals,dist_files sdk win_sdk,$(my_dist_files))
-ifdef HOST_CROSS_OS
-$(call dist-for-goals,dist_files sdk win_sdk,$(ALL_MODULES.host_cross_fastboot.BUILT))
-endif
my_dist_files :=
diff --git a/fastboot/constants.h b/fastboot/constants.h
index 705da33..81f0560 100644
--- a/fastboot/constants.h
+++ b/fastboot/constants.h
@@ -64,3 +64,4 @@
#define FB_VAR_OFF_MODE_CHARGE_STATE "off-mode-charge"
#define FB_VAR_BATTERY_VOLTAGE "battery-voltage"
#define FB_VAR_BATTERY_SOC_OK "battery-soc-ok"
+#define FB_VAR_SUPER_PARTITION_NAME "super-partition-name"
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index 6e45133..71d2a1d 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -99,7 +99,8 @@
{FB_VAR_OFF_MODE_CHARGE_STATE, {GetOffModeChargeState, nullptr}},
{FB_VAR_BATTERY_VOLTAGE, {GetBatteryVoltage, nullptr}},
{FB_VAR_BATTERY_SOC_OK, {GetBatterySoCOk, nullptr}},
- {FB_VAR_HW_REVISION, {GetHardwareRevision, nullptr}}};
+ {FB_VAR_HW_REVISION, {GetHardwareRevision, nullptr}},
+ {FB_VAR_SUPER_PARTITION_NAME, {GetSuperPartitionName, nullptr}}};
if (args.size() < 2) {
return device->WriteFail("Missing argument");
@@ -321,7 +322,7 @@
// partition table to the same place it was read.
class PartitionBuilder {
public:
- explicit PartitionBuilder(FastbootDevice* device);
+ explicit PartitionBuilder(FastbootDevice* device, const std::string& partition_name);
bool Write();
bool Valid() const { return !!builder_; }
@@ -333,15 +334,14 @@
std::unique_ptr<MetadataBuilder> builder_;
};
-PartitionBuilder::PartitionBuilder(FastbootDevice* device) {
- auto super_device = FindPhysicalPartition(fs_mgr_get_super_partition_name());
+PartitionBuilder::PartitionBuilder(FastbootDevice* device, const std::string& partition_name) {
+ std::string slot_suffix = GetSuperSlotSuffix(device, partition_name);
+ slot_number_ = SlotNumberForSlotSuffix(slot_suffix);
+ auto super_device = FindPhysicalPartition(fs_mgr_get_super_partition_name(slot_number_));
if (!super_device) {
return;
}
super_device_ = *super_device;
-
- std::string slot = device->GetCurrentSlot();
- slot_number_ = SlotNumberForSlotSuffix(slot);
builder_ = MetadataBuilder::New(super_device_, slot_number_);
}
@@ -350,7 +350,7 @@
if (!metadata) {
return false;
}
- return UpdatePartitionTable(super_device_, *metadata.get(), slot_number_);
+ return UpdateAllPartitionMetadata(super_device_, *metadata.get());
}
bool CreatePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args) {
@@ -368,7 +368,7 @@
return device->WriteFail("Invalid partition size");
}
- PartitionBuilder builder(device);
+ PartitionBuilder builder(device, partition_name);
if (!builder.Valid()) {
return device->WriteFail("Could not open super partition");
}
@@ -400,11 +400,13 @@
return device->WriteStatus(FastbootResult::FAIL, "Command not available on locked devices");
}
- PartitionBuilder builder(device);
+ std::string partition_name = args[1];
+
+ PartitionBuilder builder(device, partition_name);
if (!builder.Valid()) {
return device->WriteFail("Could not open super partition");
}
- builder->RemovePartition(args[1]);
+ builder->RemovePartition(partition_name);
if (!builder.Write()) {
return device->WriteFail("Failed to write partition table");
}
@@ -426,7 +428,7 @@
return device->WriteFail("Invalid partition size");
}
- PartitionBuilder builder(device);
+ PartitionBuilder builder(device, partition_name);
if (!builder.Valid()) {
return device->WriteFail("Could not open super partition");
}
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index 66b90bf..7b99884 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -148,20 +148,42 @@
// image.
std::string slot_suffix = device->GetCurrentSlot();
uint32_t slot_number = SlotNumberForSlotSuffix(slot_suffix);
- if (wipe || !ReadMetadata(super_name, slot_number)) {
+ std::unique_ptr<LpMetadata> old_metadata = ReadMetadata(super_name, slot_number);
+ if (wipe || !old_metadata) {
if (!FlashPartitionTable(super_name, *new_metadata.get())) {
return device->WriteFail("Unable to flash new partition table");
}
return device->WriteOkay("Successfully flashed partition table");
}
- // Write the new table to every metadata slot.
- bool ok = true;
- for (size_t i = 0; i < new_metadata->geometry.metadata_slot_count; i++) {
- ok &= UpdatePartitionTable(super_name, *new_metadata.get(), i);
+ std::set<std::string> partitions_to_keep;
+ for (const auto& partition : old_metadata->partitions) {
+ // Preserve partitions in the other slot, but not the current slot.
+ std::string partition_name = GetPartitionName(partition);
+ if (!slot_suffix.empty() && GetPartitionSlotSuffix(partition_name) == slot_suffix) {
+ continue;
+ }
+ partitions_to_keep.emplace(partition_name);
}
- if (!ok) {
+ // Do not preserve the scratch partition.
+ partitions_to_keep.erase("scratch");
+
+ if (!partitions_to_keep.empty()) {
+ std::unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(*new_metadata.get());
+ if (!builder->ImportPartitions(*old_metadata.get(), partitions_to_keep)) {
+ return device->WriteFail(
+ "Old partitions are not compatible with the new super layout; wipe needed");
+ }
+
+ new_metadata = builder->Export();
+ if (!new_metadata) {
+ return device->WriteFail("Unable to build new partition table; wipe needed");
+ }
+ }
+
+ // Write the new table to every metadata slot.
+ if (!UpdateAllPartitionMetadata(super_name, *new_metadata.get())) {
return device->WriteFail("Unable to write new partition table");
}
return device->WriteOkay("Successfully updated partition table");
diff --git a/fastboot/device/utility.cpp b/fastboot/device/utility.cpp
index b844b9f..2ae9ac5 100644
--- a/fastboot/device/utility.cpp
+++ b/fastboot/device/utility.cpp
@@ -23,9 +23,11 @@
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <android-base/strings.h>
#include <fs_mgr.h>
#include <fs_mgr_dm_linear.h>
+#include <liblp/builder.h>
#include <liblp/liblp.h>
#include "fastboot_device.h"
@@ -35,7 +37,9 @@
using android::base::unique_fd;
using android::hardware::boot::V1_0::Slot;
-static bool OpenPhysicalPartition(const std::string& name, PartitionHandle* handle) {
+namespace {
+
+bool OpenPhysicalPartition(const std::string& name, PartitionHandle* handle) {
std::optional<std::string> path = FindPhysicalPartition(name);
if (!path) {
return false;
@@ -44,28 +48,31 @@
return true;
}
-static bool OpenLogicalPartition(const std::string& name, const std::string& slot,
- PartitionHandle* handle) {
- std::optional<std::string> path = FindPhysicalPartition(fs_mgr_get_super_partition_name());
+bool OpenLogicalPartition(FastbootDevice* device, const std::string& partition_name,
+ PartitionHandle* handle) {
+ std::string slot_suffix = GetSuperSlotSuffix(device, partition_name);
+ uint32_t slot_number = SlotNumberForSlotSuffix(slot_suffix);
+ auto path = FindPhysicalPartition(fs_mgr_get_super_partition_name(slot_number));
if (!path) {
return false;
}
- uint32_t slot_number = SlotNumberForSlotSuffix(slot);
std::string dm_path;
- if (!CreateLogicalPartition(path->c_str(), slot_number, name, true, 5s, &dm_path)) {
- LOG(ERROR) << "Could not map partition: " << name;
+ if (!CreateLogicalPartition(path->c_str(), slot_number, partition_name, true, 5s, &dm_path)) {
+ LOG(ERROR) << "Could not map partition: " << partition_name;
return false;
}
- auto closer = [name]() -> void { DestroyLogicalPartition(name, 5s); };
+ auto closer = [partition_name]() -> void { DestroyLogicalPartition(partition_name, 5s); };
*handle = PartitionHandle(dm_path, std::move(closer));
return true;
}
+} // namespace
+
bool OpenPartition(FastbootDevice* device, const std::string& name, PartitionHandle* handle) {
// We prioritize logical partitions over physical ones, and do this
// consistently for other partition operations (like getvar:partition-size).
- if (LogicalPartitionExists(name, device->GetCurrentSlot())) {
- if (!OpenLogicalPartition(name, device->GetCurrentSlot(), handle)) {
+ if (LogicalPartitionExists(device, name)) {
+ if (!OpenLogicalPartition(device, name, handle)) {
return false;
}
} else if (!OpenPhysicalPartition(name, handle)) {
@@ -104,14 +111,14 @@
return nullptr;
}
-bool LogicalPartitionExists(const std::string& name, const std::string& slot_suffix,
- bool* is_zero_length) {
- auto path = FindPhysicalPartition(fs_mgr_get_super_partition_name());
+bool LogicalPartitionExists(FastbootDevice* device, const std::string& name, bool* is_zero_length) {
+ std::string slot_suffix = GetSuperSlotSuffix(device, name);
+ uint32_t slot_number = SlotNumberForSlotSuffix(slot_suffix);
+ auto path = FindPhysicalPartition(fs_mgr_get_super_partition_name(slot_number));
if (!path) {
return false;
}
- uint32_t slot_number = SlotNumberForSlotSuffix(slot_suffix);
std::unique_ptr<LpMetadata> metadata = ReadMetadata(path->c_str(), slot_number);
if (!metadata) {
return false;
@@ -154,12 +161,29 @@
}
}
- // Next get logical partitions.
- if (auto path = FindPhysicalPartition(fs_mgr_get_super_partition_name())) {
- uint32_t slot_number = SlotNumberForSlotSuffix(device->GetCurrentSlot());
- if (auto metadata = ReadMetadata(path->c_str(), slot_number)) {
- for (const auto& partition : metadata->partitions) {
- std::string partition_name = GetPartitionName(partition);
+ // Find metadata in each super partition (on retrofit devices, there will
+ // be two).
+ std::vector<std::unique_ptr<LpMetadata>> metadata_list;
+
+ uint32_t current_slot = SlotNumberForSlotSuffix(device->GetCurrentSlot());
+ std::string super_name = fs_mgr_get_super_partition_name(current_slot);
+ if (auto metadata = ReadMetadata(super_name, current_slot)) {
+ metadata_list.emplace_back(std::move(metadata));
+ }
+
+ uint32_t other_slot = (current_slot == 0) ? 1 : 0;
+ std::string other_super = fs_mgr_get_super_partition_name(other_slot);
+ if (super_name != other_super) {
+ if (auto metadata = ReadMetadata(other_super, other_slot)) {
+ metadata_list.emplace_back(std::move(metadata));
+ }
+ }
+
+ for (const auto& metadata : metadata_list) {
+ for (const auto& partition : metadata->partitions) {
+ std::string partition_name = GetPartitionName(partition);
+ if (std::find(partitions.begin(), partitions.end(), partition_name) ==
+ partitions.end()) {
partitions.emplace_back(partition_name);
}
}
@@ -175,3 +199,30 @@
}
return cmdline.find("androidboot.verifiedbootstate=orange") == std::string::npos;
}
+
+bool UpdateAllPartitionMetadata(const std::string& super_name,
+ const android::fs_mgr::LpMetadata& metadata) {
+ bool ok = true;
+ for (size_t i = 0; i < metadata.geometry.metadata_slot_count; i++) {
+ ok &= UpdatePartitionTable(super_name, metadata, i);
+ }
+ return ok;
+}
+
+std::string GetSuperSlotSuffix(FastbootDevice* device, const std::string& partition_name) {
+ // If the super partition does not have a slot suffix, this is not a
+ // retrofit device, and we should take the current slot.
+ std::string current_slot_suffix = device->GetCurrentSlot();
+ uint32_t current_slot_number = SlotNumberForSlotSuffix(current_slot_suffix);
+ std::string super_partition = fs_mgr_get_super_partition_name(current_slot_number);
+ if (GetPartitionSlotSuffix(super_partition).empty()) {
+ return current_slot_suffix;
+ }
+
+ // Otherwise, infer the slot from the partition name.
+ std::string slot_suffix = GetPartitionSlotSuffix(partition_name);
+ if (!slot_suffix.empty()) {
+ return slot_suffix;
+ }
+ return current_slot_suffix;
+}
diff --git a/fastboot/device/utility.h b/fastboot/device/utility.h
index bb08f72..4c6aa07 100644
--- a/fastboot/device/utility.h
+++ b/fastboot/device/utility.h
@@ -20,6 +20,7 @@
#include <android-base/unique_fd.h>
#include <android/hardware/boot/1.0/IBootControl.h>
+#include <liblp/liblp.h>
// Logical partitions are only mapped to a block device as needed, and
// immediately unmapped when no longer needed. In order to enforce this we
@@ -52,10 +53,20 @@
class FastbootDevice;
+// On normal devices, the super partition is always named "super". On retrofit
+// devices, the name must be derived from the partition name or current slot.
+// This helper assists in choosing the correct super for a given partition
+// name.
+std::string GetSuperSlotSuffix(FastbootDevice* device, const std::string& partition_name);
+
std::optional<std::string> FindPhysicalPartition(const std::string& name);
-bool LogicalPartitionExists(const std::string& name, const std::string& slot_suffix,
+bool LogicalPartitionExists(FastbootDevice* device, const std::string& name,
bool* is_zero_length = nullptr);
bool OpenPartition(FastbootDevice* device, const std::string& name, PartitionHandle* handle);
bool GetSlotNumber(const std::string& slot, android::hardware::boot::V1_0::Slot* number);
std::vector<std::string> ListPartitions(FastbootDevice* device);
bool GetDeviceLockStatus();
+
+// Update all copies of metadata.
+bool UpdateAllPartitionMetadata(const std::string& super_name,
+ const android::fs_mgr::LpMetadata& metadata);
diff --git a/fastboot/device/variables.cpp b/fastboot/device/variables.cpp
index cbd2856..130a3cf 100644
--- a/fastboot/device/variables.cpp
+++ b/fastboot/device/variables.cpp
@@ -24,7 +24,9 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <ext4_utils/ext4_utils.h>
+#include <fs_mgr.h>
#include <healthhalutils/HealthHalUtils.h>
+#include <liblp/liblp.h>
#include "fastboot_device.h"
#include "flashing.h"
@@ -35,6 +37,7 @@
using ::android::hardware::fastboot::V1_0::FileSystemType;
using ::android::hardware::fastboot::V1_0::Result;
using ::android::hardware::fastboot::V1_0::Status;
+using namespace android::fs_mgr;
constexpr char kFastbootProtocolVersion[] = "0.4";
@@ -268,8 +271,7 @@
return true;
}
std::string partition_name = args[0] + slot_suffix;
- if (FindPhysicalPartition(partition_name) ||
- LogicalPartitionExists(partition_name, slot_suffix)) {
+ if (FindPhysicalPartition(partition_name) || LogicalPartitionExists(device, partition_name)) {
*message = "yes";
} else {
*message = "no";
@@ -286,8 +288,7 @@
// Zero-length partitions cannot be created through device-mapper, so we
// special case them here.
bool is_zero_length;
- if (LogicalPartitionExists(args[0], device->GetCurrentSlot(), &is_zero_length) &&
- is_zero_length) {
+ if (LogicalPartitionExists(device, args[0], &is_zero_length) && is_zero_length) {
*message = "0x0";
return true;
}
@@ -310,8 +311,7 @@
}
std::string partition_name = args[0];
- if (!FindPhysicalPartition(partition_name) &&
- !LogicalPartitionExists(partition_name, device->GetCurrentSlot())) {
+ if (!FindPhysicalPartition(partition_name) && !LogicalPartitionExists(device, partition_name)) {
*message = "Invalid partition";
return false;
}
@@ -360,7 +360,7 @@
// return "true", to be consistent with prefering to flash logical partitions
// over physical ones.
std::string partition_name = args[0];
- if (LogicalPartitionExists(partition_name, device->GetCurrentSlot())) {
+ if (LogicalPartitionExists(device, partition_name)) {
*message = "yes";
return true;
}
@@ -417,3 +417,10 @@
*message = android::base::GetProperty("ro.revision", "");
return true;
}
+
+bool GetSuperPartitionName(FastbootDevice* device, const std::vector<std::string>& /* args */,
+ std::string* message) {
+ uint32_t slot_number = SlotNumberForSlotSuffix(device->GetCurrentSlot());
+ *message = fs_mgr_get_super_partition_name(slot_number);
+ return true;
+}
diff --git a/fastboot/device/variables.h b/fastboot/device/variables.h
index 59b71e8..015a4c5 100644
--- a/fastboot/device/variables.h
+++ b/fastboot/device/variables.h
@@ -59,6 +59,8 @@
std::string* message);
bool GetBatterySoCOk(FastbootDevice* device, const std::vector<std::string>& args,
std::string* message);
+bool GetSuperPartitionName(FastbootDevice* device, const std::vector<std::string>& args,
+ std::string* message);
// Helpers for getvar all.
std::vector<std::vector<std::string>> GetAllPartitionArgsWithSlot(FastbootDevice* device);
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 3c6b1b7..e066bff 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -56,9 +56,9 @@
#include <android-base/parsenetaddress.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-#include <android-base/test_utils.h>
#include <android-base/unique_fd.h>
#include <build/version.h>
+#include <liblp/liblp.h>
#include <platform_tools_version.h>
#include <sparse/sparse.h>
#include <ziparchive/zip_archive.h>
@@ -408,6 +408,7 @@
" -s SERIAL Specify a USB device.\n"
" -s tcp|udp:HOST[:PORT] Specify a network device.\n"
" -S SIZE[K|M|G] Break into sparse files no larger than SIZE.\n"
+ " --force Force a flash operation that may be unsafe.\n"
" --slot SLOT Use SLOT; 'all' for both slots, 'other' for\n"
" non-current slot (default: current active slot).\n"
" --set-active[=SLOT] Sets the active slot before rebooting.\n"
@@ -1129,25 +1130,6 @@
return fb->GetVar("is-userspace", &value) == fastboot::SUCCESS && value == "yes";
}
-static bool if_partition_exists(const std::string& partition, const std::string& slot) {
- std::string has_slot;
- std::string partition_name = partition;
-
- if (fb->GetVar("has-slot:" + partition, &has_slot) == fastboot::SUCCESS && has_slot == "yes") {
- if (slot == "") {
- std::string current_slot = get_current_slot();
- if (current_slot == "") {
- die("Failed to identify current slot");
- }
- partition_name += "_" + current_slot;
- } else {
- partition_name += "_" + slot;
- }
- }
- std::string partition_size;
- return fb->GetVar("partition-size:" + partition_name, &partition_size) == fastboot::SUCCESS;
-}
-
static void reboot_to_userspace_fastboot() {
fb->RebootTo("fastboot");
@@ -1200,6 +1182,15 @@
void FlashAllTool::Flash() {
DumpInfo();
CheckRequirements();
+
+ // Change the slot first, so we boot into the correct recovery image when
+ // using fastbootd.
+ if (slot_override_ == "all") {
+ set_active("a");
+ } else {
+ set_active(slot_override_);
+ }
+
DetermineSecondarySlot();
CollectImages();
@@ -1223,12 +1214,6 @@
// Flash OS images, resizing logical partitions as needed.
FlashImages(os_images_);
-
- if (slot_override_ == "all") {
- set_active("a");
- } else {
- set_active(slot_override_);
- }
}
void FlashAllTool::CheckRequirements() {
@@ -1243,7 +1228,7 @@
if (skip_secondary_) {
return;
}
- if (slot_override_ != "") {
+ if (slot_override_ != "" && slot_override_ != "all") {
secondary_slot_ = get_other_slot(slot_override_);
} else {
secondary_slot_ = get_other_slot();
@@ -1304,10 +1289,6 @@
}
void FlashAllTool::UpdateSuperPartition() {
- if (!if_partition_exists("super", "")) {
- return;
- }
-
int fd = source_.OpenFile("super_empty.img");
if (fd < 0) {
return;
@@ -1318,9 +1299,14 @@
if (!is_userspace_fastboot()) {
die("Failed to boot into userspace; one or more components might be unbootable.");
}
- fb->Download("super", fd, get_file_size(fd));
- std::string command = "update-super:super";
+ std::string super_name;
+ if (fb->GetVar("super-partition-name", &super_name) != fastboot::RetCode::SUCCESS) {
+ super_name = "super";
+ }
+ fb->Download(super_name, fd, get_file_size(fd));
+
+ std::string command = "update-super:" + super_name;
if (wipe_) {
command += ":wipe";
}
@@ -1521,6 +1507,31 @@
fprintf(stderr, "FAILED (%s)\n", fb->Error().c_str());
}
+static bool should_flash_in_userspace(const std::string& partition_name) {
+ auto path = find_item_given_name("super_empty.img");
+ if (path.empty()) {
+ return false;
+ }
+ auto metadata = android::fs_mgr::ReadFromImageFile(path);
+ if (!metadata) {
+ return false;
+ }
+ for (const auto& partition : metadata->partitions) {
+ auto candidate = android::fs_mgr::GetPartitionName(partition);
+ if (partition.attributes & LP_PARTITION_ATTR_SLOT_SUFFIXED) {
+ // On retrofit devices, we don't know if, or whether, the A or B
+ // slot has been flashed for dynamic partitions. Instead we add
+ // both names to the list as a conservative guess.
+ if (candidate + "_a" == partition_name || candidate + "_b" == partition_name) {
+ return true;
+ }
+ } else if (candidate == partition_name) {
+ return true;
+ }
+ }
+ return false;
+}
+
int FastBootTool::Main(int argc, char* argv[]) {
bool wants_wipe = false;
bool wants_reboot = false;
@@ -1531,6 +1542,7 @@
bool wants_set_active = false;
bool skip_secondary = false;
bool set_fbe_marker = false;
+ bool force_flash = false;
int longindex;
std::string slot_override;
std::string next_active;
@@ -1546,6 +1558,7 @@
{"cmdline", required_argument, 0, 0},
{"disable-verification", no_argument, 0, 0},
{"disable-verity", no_argument, 0, 0},
+ {"force", no_argument, 0, 0},
{"header-version", required_argument, 0, 0},
{"help", no_argument, 0, 'h'},
{"kernel-offset", required_argument, 0, 0},
@@ -1581,6 +1594,8 @@
g_disable_verification = true;
} else if (name == "disable-verity") {
g_disable_verity = true;
+ } else if (name == "force") {
+ force_flash = true;
} else if (name == "header-version") {
g_boot_img_hdr.header_version = strtoul(optarg, nullptr, 0);
} else if (name == "kernel-offset") {
@@ -1795,6 +1810,16 @@
if (fname.empty()) die("cannot determine image filename for '%s'", pname.c_str());
auto flash = [&](const std::string &partition) {
+ if (should_flash_in_userspace(partition) && !is_userspace_fastboot() &&
+ !force_flash) {
+ die("The partition you are trying to flash is dynamic, and "
+ "should be flashed via fastbootd. Please run:\n"
+ "\n"
+ " fastboot reboot fastboot\n"
+ "\n"
+ "And try again. If you are intentionally trying to "
+ "overwrite a fixed partition, use --force.");
+ }
do_flash(partition.c_str(), fname.c_str());
};
do_for_partitions(pname.c_str(), slot_override, flash, true);
diff --git a/fastboot/fuzzy_fastboot/Android.bp b/fastboot/fuzzy_fastboot/Android.bp
index 301534b..277cc3a 100644
--- a/fastboot/fuzzy_fastboot/Android.bp
+++ b/fastboot/fuzzy_fastboot/Android.bp
@@ -26,6 +26,9 @@
"libadb_host",
"libtinyxml2",
"libsparse",
+ "liblp",
+ "libcrypto",
+ "libext4_utils",
],
// Static libs (libfastboot2) shared library dependencies are not transitively included
diff --git a/fastboot/usb_osx.cpp b/fastboot/usb_osx.cpp
index ed02c4a..f597f50 100644
--- a/fastboot/usb_osx.cpp
+++ b/fastboot/usb_osx.cpp
@@ -61,7 +61,7 @@
UInt8 bulkIn;
UInt8 bulkOut;
- IOUSBInterfaceInterface190 **interface;
+ IOUSBInterfaceInterface500** interface;
unsigned int zero_mask;
};
@@ -86,13 +86,13 @@
/** Try out all the interfaces and see if there's a match. Returns 0 on
* success, -1 on failure. */
-static int try_interfaces(IOUSBDeviceInterface182 **dev, usb_handle *handle) {
+static int try_interfaces(IOUSBDeviceInterface500** dev, usb_handle* handle) {
IOReturn kr;
IOUSBFindInterfaceRequest request;
io_iterator_t iterator;
io_service_t usbInterface;
IOCFPlugInInterface **plugInInterface;
- IOUSBInterfaceInterface190 **interface = NULL;
+ IOUSBInterfaceInterface500** interface = NULL;
HRESULT result;
SInt32 score;
UInt8 interfaceNumEndpoints;
@@ -128,10 +128,10 @@
}
// Now create the interface interface for the interface
- result = (*plugInInterface)->QueryInterface(
- plugInInterface,
- CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID),
- (LPVOID*) &interface);
+ result = (*plugInInterface)
+ ->QueryInterface(plugInInterface,
+ CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID500),
+ (LPVOID*)&interface);
// No longer need the intermediate plugin
(*plugInInterface)->Release(plugInInterface);
@@ -270,7 +270,7 @@
static int try_device(io_service_t device, usb_handle *handle) {
kern_return_t kr;
IOCFPlugInInterface **plugin = NULL;
- IOUSBDeviceInterface182 **dev = NULL;
+ IOUSBDeviceInterface500** dev = NULL;
SInt32 score;
HRESULT result;
UInt8 serialIndex;
@@ -287,8 +287,8 @@
}
// Now create the device interface.
- result = (*plugin)->QueryInterface(plugin,
- CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID*) &dev);
+ result = (*plugin)->QueryInterface(plugin, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID500),
+ (LPVOID*)&dev);
if ((result != 0) || (dev == NULL)) {
ERR("Couldn't create a device interface (%08x)\n", (int) result);
goto error;
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 3cce0e8..6c8a943 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -46,6 +46,7 @@
"fs_mgr_avb_ops.cpp",
"fs_mgr_dm_linear.cpp",
"fs_mgr_overlayfs.cpp",
+ "fs_mgr_vendor_overlay.cpp",
],
shared_libs: [
"libbase",
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index a6ef35b..8e57e1e 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -50,7 +50,6 @@
#include <cutils/partition_utils.h>
#include <cutils/properties.h>
#include <ext4_utils/ext4.h>
-#include <ext4_utils/ext4_crypt_init_extensions.h>
#include <ext4_utils/ext4_sb.h>
#include <ext4_utils/ext4_utils.h>
#include <ext4_utils/wipe.h>
@@ -1032,7 +1031,7 @@
if (fstab->recs[i].fs_mgr_flags & MF_AVB) {
if (!avb_handle) {
- avb_handle = FsManagerAvbHandle::Open(*fstab);
+ avb_handle = FsManagerAvbHandle::Open();
if (!avb_handle) {
LERROR << "Failed to open FsManagerAvbHandle";
return FS_MGR_MNTALL_FAIL;
@@ -1276,7 +1275,7 @@
if (fstab->recs[i].fs_mgr_flags & MF_AVB) {
if (!avb_handle) {
- avb_handle = FsManagerAvbHandle::Open(*fstab);
+ avb_handle = FsManagerAvbHandle::Open();
if (!avb_handle) {
LERROR << "Failed to open FsManagerAvbHandle";
return FS_MGR_DOMNT_FAILED;
@@ -1589,6 +1588,25 @@
return true;
}
-std::string fs_mgr_get_super_partition_name(int /* slot */) {
+std::string fs_mgr_get_super_partition_name(int slot) {
+ // Devices upgrading to dynamic partitions are allowed to specify a super
+ // partition name, assumed to be A/B (non-A/B retrofit is not supported).
+ // For devices launching with dynamic partition support, the partition
+ // name must be "super".
+ std::string super_partition;
+ if (fs_mgr_get_boot_config_from_kernel_cmdline("super_partition", &super_partition)) {
+ std::string suffix;
+ if (slot == 0) {
+ suffix = "_a";
+ } else if (slot == 1) {
+ suffix = "_b";
+ } else if (slot == -1) {
+ suffix = fs_mgr_get_slot_suffix();
+ }
+ if (suffix.empty()) {
+ LFATAL << "Super partition name can only be overridden on A/B devices.";
+ }
+ return super_partition + suffix;
+ }
return LP_METADATA_DEFAULT_PARTITION_NAME;
}
diff --git a/fs_mgr/fs_mgr_avb.cpp b/fs_mgr/fs_mgr_avb.cpp
index 7c6093e..6f94d45 100644
--- a/fs_mgr/fs_mgr_avb.cpp
+++ b/fs_mgr/fs_mgr_avb.cpp
@@ -322,17 +322,6 @@
continue;
}
- // Ensures that hashtree descriptor is in /vbmeta or /boot or in
- // the same partition for verity setup.
- std::string vbmeta_partition_name(verify_data.vbmeta_images[i].partition_name);
- if (vbmeta_partition_name != "vbmeta" &&
- vbmeta_partition_name != "boot" && // for legacy device to append top-level vbmeta
- vbmeta_partition_name != partition_name) {
- LWARNING << "Skip vbmeta image at " << verify_data.vbmeta_images[i].partition_name
- << " for partition: " << partition_name.c_str();
- continue;
- }
-
for (size_t j = 0; j < num_descriptors && !found; j++) {
AvbDescriptor desc;
if (!avb_descriptor_validate_and_byteswap(descriptors[j], &desc)) {
@@ -372,21 +361,7 @@
return true;
}
-FsManagerAvbUniquePtr FsManagerAvbHandle::Open(const fstab& fstab) {
- FsManagerAvbOps avb_ops(fstab);
- return DoOpen(&avb_ops);
-}
-
-FsManagerAvbUniquePtr FsManagerAvbHandle::Open(ByNameSymlinkMap&& by_name_symlink_map) {
- if (by_name_symlink_map.empty()) {
- LERROR << "Empty by_name_symlink_map when opening FsManagerAvbHandle";
- return nullptr;
- }
- FsManagerAvbOps avb_ops(std::move(by_name_symlink_map));
- return DoOpen(&avb_ops);
-}
-
-FsManagerAvbUniquePtr FsManagerAvbHandle::DoOpen(FsManagerAvbOps* avb_ops) {
+FsManagerAvbUniquePtr FsManagerAvbHandle::Open() {
bool is_device_unlocked = fs_mgr_is_device_unlocked();
FsManagerAvbUniquePtr avb_handle(new FsManagerAvbHandle());
@@ -395,10 +370,11 @@
return nullptr;
}
+ FsManagerAvbOps avb_ops;
AvbSlotVerifyFlags flags = is_device_unlocked ? AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR
: AVB_SLOT_VERIFY_FLAGS_NONE;
AvbSlotVerifyResult verify_result =
- avb_ops->AvbSlotVerify(fs_mgr_get_slot_suffix(), flags, &avb_handle->avb_slot_data_);
+ avb_ops.AvbSlotVerify(fs_mgr_get_slot_suffix(), flags, &avb_handle->avb_slot_data_);
// Only allow two verify results:
// - AVB_SLOT_VERIFY_RESULT_OK.
diff --git a/fs_mgr/fs_mgr_avb_ops.cpp b/fs_mgr/fs_mgr_avb_ops.cpp
index 43879fe..18efa22 100644
--- a/fs_mgr/fs_mgr_avb_ops.cpp
+++ b/fs_mgr/fs_mgr_avb_ops.cpp
@@ -40,6 +40,8 @@
#include "fs_mgr.h"
#include "fs_mgr_priv.h"
+using namespace std::literals;
+
static AvbIOResult read_from_partition(AvbOps* ops, const char* partition, int64_t offset,
size_t num_bytes, void* buffer, size_t* out_num_read) {
return FsManagerAvbOps::GetInstanceFromAvbOps(ops)->ReadFromPartition(
@@ -98,7 +100,7 @@
return AVB_IO_RESULT_OK;
}
-void FsManagerAvbOps::InitializeAvbOps() {
+FsManagerAvbOps::FsManagerAvbOps() {
// We only need to provide the implementation of read_from_partition()
// operation since that's all what is being used by the avb_slot_verify().
// Other I/O operations are only required in bootloader but not in
@@ -116,31 +118,10 @@
avb_ops_.user_data = this;
}
-FsManagerAvbOps::FsManagerAvbOps(std::map<std::string, std::string>&& by_name_symlink_map)
- : by_name_symlink_map_(std::move(by_name_symlink_map)) {
- InitializeAvbOps();
-}
-
-FsManagerAvbOps::FsManagerAvbOps(const fstab& fstab) {
- // Constructs the by-name symlink map for each fstab record.
- // /dev/block/platform/soc.0/7824900.sdhci/by-name/system_a =>
- // by_name_symlink_map_["system_a"] = "/dev/block/platform/soc.0/7824900.sdhci/by-name/system_a"
- for (int i = 0; i < fstab.num_entries; i++) {
- std::string partition_name = basename(fstab.recs[i].blk_device);
- by_name_symlink_map_[partition_name] = fstab.recs[i].blk_device;
- }
- InitializeAvbOps();
-}
-
AvbIOResult FsManagerAvbOps::ReadFromPartition(const char* partition, int64_t offset,
size_t num_bytes, void* buffer,
size_t* out_num_read) {
- const auto iter = by_name_symlink_map_.find(partition);
- if (iter == by_name_symlink_map_.end()) {
- LERROR << "by-name symlink not found for partition: '" << partition << "'";
- return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
- }
- std::string path = iter->second;
+ const std::string path = "/dev/block/by-name/"s + partition;
// Ensures the device path (a symlink created by init) is ready to access.
if (!fs_mgr_wait_for_file(path, 1s)) {
diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp
index 6ddd5a8..fe0e039 100644
--- a/fs_mgr/fs_mgr_dm_linear.cpp
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -75,14 +75,9 @@
target = std::make_unique<DmTargetZero>(sector, extent.num_sectors);
break;
case LP_TARGET_TYPE_LINEAR: {
- auto block_device = GetMetadataSuperBlockDevice(metadata);
- if (!block_device) {
- LOG(ERROR) << "Could not identify the super block device";
- return false;
- }
-
+ const auto& block_device = metadata.block_devices[extent.target_source];
std::string path;
- if (!GetPhysicalPartitionDevicePath(*block_device, &path)) {
+ if (!GetPhysicalPartitionDevicePath(block_device, &path)) {
LOG(ERROR) << "Unable to complete device-mapper table, unknown block device";
return false;
}
@@ -142,13 +137,22 @@
LOG(ERROR) << "Could not read partition table.";
return true;
}
- for (const auto& partition : metadata->partitions) {
+ return CreateLogicalPartitions(*metadata.get());
+}
+
+std::unique_ptr<LpMetadata> ReadCurrentMetadata(const std::string& block_device) {
+ uint32_t slot = SlotNumberForSlotSuffix(fs_mgr_get_slot_suffix());
+ return ReadMetadata(block_device.c_str(), slot);
+}
+
+bool CreateLogicalPartitions(const LpMetadata& metadata) {
+ for (const auto& partition : metadata.partitions) {
if (!partition.num_extents) {
LINFO << "Skipping zero-length logical partition: " << GetPartitionName(partition);
continue;
}
std::string path;
- if (!CreateLogicalPartition(*metadata.get(), partition, false, {}, &path)) {
+ if (!CreateLogicalPartition(metadata, partition, false, {}, &path)) {
LERROR << "Could not create logical partition: " << GetPartitionName(partition);
return false;
}
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index fc3a05c..e89c91c 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -403,10 +403,13 @@
static bool is_dt_fstab_compatible() {
std::string dt_value;
std::string file_name = get_android_dt_dir() + "/fstab/compatible";
- if (read_dt_file(file_name, &dt_value)) {
- if (dt_value == "android,fstab") {
- return true;
- }
+
+ if (read_dt_file(file_name, &dt_value) && dt_value == "android,fstab") {
+ // If there's no status property or its set to "ok" or "okay", then we use the DT fstab.
+ std::string status_value;
+ std::string status_file_name = get_android_dt_dir() + "/fstab/status";
+ return !read_dt_file(status_file_name, &status_value) || status_value == "ok" ||
+ status_value == "okay";
}
return false;
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index ad488a9..a1de005 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -20,12 +20,14 @@
#include <linux/fs.h>
#include <selinux/selinux.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/types.h>
+#include <sys/utsname.h>
#include <sys/vfs.h>
#include <unistd.h>
@@ -41,6 +43,7 @@
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <ext4_utils/ext4_utils.h>
+#include <fs_mgr.h>
#include <fs_mgr_dm_linear.h>
#include <fs_mgr_overlayfs.h>
#include <fstab/fstab.h>
@@ -54,13 +57,32 @@
using namespace android::dm;
using namespace android::fs_mgr;
+namespace {
+
+bool fs_mgr_access(const std::string& path) {
+ auto save_errno = errno;
+ auto ret = access(path.c_str(), F_OK) == 0;
+ errno = save_errno;
+ return ret;
+}
+
+} // namespace
+
#if ALLOW_ADBD_DISABLE_VERITY == 0 // If we are a user build, provide stubs
-bool fs_mgr_overlayfs_mount_all(const fstab*) {
+bool fs_mgr_overlayfs_mount_all(fstab*) {
return false;
}
-std::vector<std::string> fs_mgr_overlayfs_required_devices(const fstab*) {
+bool fs_mgr_overlayfs_mount_all(const std::vector<const fstab_rec*>&) {
+ return false;
+}
+
+std::vector<std::string> fs_mgr_overlayfs_required_devices(fstab*) {
+ return {};
+}
+
+std::vector<std::string> fs_mgr_overlayfs_required_devices(const std::vector<const fstab_rec*>&) {
return {};
}
@@ -103,17 +125,6 @@
return ret | !rmdir(test_directory.c_str());
}
-std::string fs_mgr_get_context(const std::string& mount_point) {
- char* ctx = nullptr;
- auto len = getfilecon(mount_point.c_str(), &ctx);
- if ((len > 0) && ctx) {
- std::string context(ctx, len);
- free(ctx);
- return context;
- }
- return "";
-}
-
// At less than 1% free space return value of false,
// means we will try to wrap with overlayfs.
bool fs_mgr_filesystem_has_space(const char* mount_point) {
@@ -127,138 +138,17 @@
return (vst.f_bfree >= (vst.f_blocks * kPercentThreshold / 100));
}
-bool fs_mgr_overlayfs_enabled(const struct fstab_rec* fsrec) {
+bool fs_mgr_overlayfs_enabled(struct fstab_rec* fsrec) {
// readonly filesystem, can not be mount -o remount,rw
// if squashfs or if free space is (near) zero making such a remount
// virtually useless, or if there are shared blocks that prevent remount,rw
- return ("squashfs"s == fsrec->fs_type) ||
- fs_mgr_has_shared_blocks(fsrec->mount_point, fsrec->blk_device) ||
- !fs_mgr_filesystem_has_space(fsrec->mount_point);
-}
-
-const auto kUpperName = "upper"s;
-const auto kWorkName = "work"s;
-const auto kOverlayTopDir = "/overlay"s;
-
-std::string fs_mgr_get_overlayfs_candidate(const std::string& mount_point) {
- if (!fs_mgr_is_dir(mount_point)) return "";
- const auto base = android::base::Basename(mount_point) + "/";
- for (const auto& overlay_mount_point : kOverlayMountPoints) {
- auto dir = overlay_mount_point + kOverlayTopDir + "/" + base;
- auto upper = dir + kUpperName;
- if (!fs_mgr_is_dir(upper)) continue;
- auto work = dir + kWorkName;
- if (!fs_mgr_is_dir(work)) continue;
- if (!fs_mgr_dir_is_writable(work)) continue;
- return dir;
+ if (("squashfs"s == fsrec->fs_type) || !fs_mgr_filesystem_has_space(fsrec->mount_point)) {
+ return true;
}
- return "";
-}
-
-const auto kLowerdirOption = "lowerdir="s;
-const auto kUpperdirOption = "upperdir="s;
-
-// default options for mount_point, returns empty string for none available.
-std::string fs_mgr_get_overlayfs_options(const std::string& mount_point) {
- auto candidate = fs_mgr_get_overlayfs_candidate(mount_point);
- if (candidate.empty()) return "";
-
- return "override_creds=off," + kLowerdirOption + mount_point + "," + kUpperdirOption +
- candidate + kUpperName + ",workdir=" + candidate + kWorkName;
-}
-
-const char* fs_mgr_mount_point(const char* mount_point) {
- if (!mount_point) return mount_point;
- if ("/"s != mount_point) return mount_point;
- return "/system";
-}
-
-bool fs_mgr_access(const std::string& path) {
- auto save_errno = errno;
- auto ret = access(path.c_str(), F_OK) == 0;
- errno = save_errno;
- return ret;
-}
-
-bool fs_mgr_rw_access(const std::string& path) {
- if (path.empty()) return false;
- auto save_errno = errno;
- auto ret = access(path.c_str(), R_OK | W_OK) == 0;
- errno = save_errno;
- return ret;
-}
-
-// return true if system supports overlayfs
-bool fs_mgr_wants_overlayfs() {
- // Properties will return empty on init first_stage_mount, so speculative
- // determination, empty (unset) _or_ "1" is true which differs from the
- // official ro.debuggable policy. ALLOW_ADBD_DISABLE_VERITY == 0 should
- // protect us from false in any case, so this is insurance.
- auto debuggable = android::base::GetProperty("ro.debuggable", "1");
- if (debuggable != "1") return false;
-
- // Overlayfs available in the kernel, and patched for override_creds?
- return fs_mgr_access("/sys/module/overlay/parameters/override_creds");
-}
-
-bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point, bool overlay_only = true) {
- std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab("/proc/mounts"),
- fs_mgr_free_fstab);
- if (!fstab) return false;
- const auto lowerdir = kLowerdirOption + mount_point;
- for (auto i = 0; i < fstab->num_entries; ++i) {
- const auto fsrec = &fstab->recs[i];
- const auto fs_type = fsrec->fs_type;
- if (!fs_type) continue;
- if (overlay_only && ("overlay"s != fs_type) && ("overlayfs"s != fs_type)) continue;
- auto fsrec_mount_point = fsrec->mount_point;
- if (!fsrec_mount_point) continue;
- if (mount_point != fsrec_mount_point) continue;
- if (!overlay_only) return true;
- const auto fs_options = fsrec->fs_options;
- if (!fs_options) continue;
- const auto options = android::base::Split(fs_options, ",");
- for (const auto& opt : options) {
- if (opt == lowerdir) {
- return true;
- }
- }
+ if (fs_mgr_is_logical(fsrec)) {
+ fs_mgr_update_logical_partition(fsrec);
}
- return false;
-}
-
-std::vector<std::string> fs_mgr_overlayfs_verity_enabled_list() {
- std::vector<std::string> ret;
- fs_mgr_update_verity_state([&ret](fstab_rec*, const char* mount_point, int, int) {
- ret.emplace_back(mount_point);
- });
- return ret;
-}
-
-bool fs_mgr_wants_overlayfs(const fstab_rec* fsrec) {
- if (!fsrec) return false;
-
- auto fsrec_mount_point = fsrec->mount_point;
- if (!fsrec_mount_point || !fsrec_mount_point[0]) return false;
- if (!fsrec->blk_device) return false;
-
- if (!fsrec->fs_type) return false;
-
- // Don't check entries that are managed by vold.
- if (fsrec->fs_mgr_flags & (MF_VOLDMANAGED | MF_RECOVERYONLY)) return false;
-
- // Only concerned with readonly partitions.
- if (!(fsrec->flags & MS_RDONLY)) return false;
-
- // If unbindable, do not allow overlayfs as this could expose us to
- // security issues. On Android, this could also be used to turn off
- // the ability to overlay an otherwise acceptable filesystem since
- // /system and /vendor are never bound(sic) to.
- if (fsrec->flags & MS_UNBINDABLE) return false;
-
- if (!fs_mgr_overlayfs_enabled(fsrec)) return false;
-
- return true;
+ return fs_mgr_has_shared_blocks(fsrec->mount_point, fsrec->blk_device);
}
bool fs_mgr_rm_all(const std::string& path, bool* change = nullptr, int level = 0) {
@@ -307,6 +197,113 @@
return ret;
}
+const auto kUpperName = "upper"s;
+const auto kWorkName = "work"s;
+const auto kOverlayTopDir = "/overlay"s;
+
+std::string fs_mgr_get_overlayfs_candidate(const std::string& mount_point) {
+ if (!fs_mgr_is_dir(mount_point)) return "";
+ const auto base = android::base::Basename(mount_point) + "/";
+ for (const auto& overlay_mount_point : kOverlayMountPoints) {
+ auto dir = overlay_mount_point + kOverlayTopDir + "/" + base;
+ auto upper = dir + kUpperName;
+ if (!fs_mgr_is_dir(upper)) continue;
+ auto work = dir + kWorkName;
+ if (!fs_mgr_is_dir(work)) continue;
+ if (!fs_mgr_dir_is_writable(work)) continue;
+ return dir;
+ }
+ return "";
+}
+
+const auto kLowerdirOption = "lowerdir="s;
+const auto kUpperdirOption = "upperdir="s;
+
+// default options for mount_point, returns empty string for none available.
+std::string fs_mgr_get_overlayfs_options(const std::string& mount_point) {
+ auto candidate = fs_mgr_get_overlayfs_candidate(mount_point);
+ if (candidate.empty()) return "";
+ auto ret = kLowerdirOption + mount_point + "," + kUpperdirOption + candidate + kUpperName +
+ ",workdir=" + candidate + kWorkName;
+ if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kOverrideCredsRequired) {
+ ret += ",override_creds=off";
+ }
+ return ret;
+}
+
+const char* fs_mgr_mount_point(const char* mount_point) {
+ if (!mount_point) return mount_point;
+ if ("/"s != mount_point) return mount_point;
+ return "/system";
+}
+
+bool fs_mgr_rw_access(const std::string& path) {
+ if (path.empty()) return false;
+ auto save_errno = errno;
+ auto ret = access(path.c_str(), R_OK | W_OK) == 0;
+ errno = save_errno;
+ return ret;
+}
+
+bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point, bool overlay_only = true) {
+ std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab("/proc/mounts"),
+ fs_mgr_free_fstab);
+ if (!fstab) return false;
+ const auto lowerdir = kLowerdirOption + mount_point;
+ for (auto i = 0; i < fstab->num_entries; ++i) {
+ const auto fsrec = &fstab->recs[i];
+ const auto fs_type = fsrec->fs_type;
+ if (!fs_type) continue;
+ if (overlay_only && ("overlay"s != fs_type) && ("overlayfs"s != fs_type)) continue;
+ auto fsrec_mount_point = fsrec->mount_point;
+ if (!fsrec_mount_point) continue;
+ if (mount_point != fsrec_mount_point) continue;
+ if (!overlay_only) return true;
+ const auto fs_options = fsrec->fs_options;
+ if (!fs_options) continue;
+ const auto options = android::base::Split(fs_options, ",");
+ for (const auto& opt : options) {
+ if (opt == lowerdir) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+std::vector<std::string> fs_mgr_overlayfs_verity_enabled_list() {
+ std::vector<std::string> ret;
+ fs_mgr_update_verity_state([&ret](fstab_rec*, const char* mount_point, int, int) {
+ ret.emplace_back(mount_point);
+ });
+ return ret;
+}
+
+bool fs_mgr_wants_overlayfs(fstab_rec* fsrec) {
+ if (!fsrec) return false;
+
+ auto fsrec_mount_point = fsrec->mount_point;
+ if (!fsrec_mount_point || !fsrec_mount_point[0]) return false;
+ if (!fsrec->blk_device) return false;
+
+ if (!fsrec->fs_type) return false;
+
+ // Don't check entries that are managed by vold.
+ if (fsrec->fs_mgr_flags & (MF_VOLDMANAGED | MF_RECOVERYONLY)) return false;
+
+ // Only concerned with readonly partitions.
+ if (!(fsrec->flags & MS_RDONLY)) return false;
+
+ // If unbindable, do not allow overlayfs as this could expose us to
+ // security issues. On Android, this could also be used to turn off
+ // the ability to overlay an otherwise acceptable filesystem since
+ // /system and /vendor are never bound(sic) to.
+ if (fsrec->flags & MS_UNBINDABLE) return false;
+
+ if (!fs_mgr_overlayfs_enabled(fsrec)) return false;
+
+ return true;
+}
constexpr char kOverlayfsFileContext[] = "u:object_r:overlayfs_file:s0";
bool fs_mgr_overlayfs_setup_dir(const std::string& dir, std::string* overlay, bool* change) {
@@ -532,8 +529,7 @@
}
}
-std::vector<std::string> fs_mgr_candidate_list(const fstab* fstab,
- const char* mount_point = nullptr) {
+std::vector<std::string> fs_mgr_candidate_list(fstab* fstab, const char* mount_point = nullptr) {
std::vector<std::string> mounts;
if (!fstab) return mounts;
@@ -705,13 +701,21 @@
errno = 0;
}
- auto ret = system((mnt_type == "f2fs")
- ? ((kMkF2fs + " -d1 " + scratch_device).c_str())
- : ((kMkExt4 + " -b 4096 -t ext4 -m 0 -M " + kScratchMountPoint +
- " -O has_journal " + scratch_device)
- .c_str()));
+ // Force mkfs by design for overlay support of adb remount, simplify and
+ // thus do not rely on fsck to correct problems that could creep in.
+ auto command = ""s;
+ if (mnt_type == "f2fs") {
+ command = kMkF2fs + " -w 4096 -f -d1 -l" + android::base::Basename(kScratchMountPoint);
+ } else if (mnt_type == "ext4") {
+ command = kMkExt4 + " -b 4096 -t ext4 -m 0 -O has_journal -M " + kScratchMountPoint;
+ } else {
+ LERROR << mnt_type << " has no mkfs cookbook";
+ return false;
+ }
+ command += " " + scratch_device;
+ auto ret = system(command.c_str());
if (ret) {
- LERROR << "make " << mnt_type << " filesystem on " << scratch_device << " error=" << ret;
+ LERROR << "make " << mnt_type << " filesystem on " << scratch_device << " return=" << ret;
return false;
}
@@ -734,10 +738,10 @@
} // namespace
-bool fs_mgr_overlayfs_mount_all(const fstab* fstab) {
+bool fs_mgr_overlayfs_mount_all(fstab* fstab) {
auto ret = false;
- if (!fs_mgr_wants_overlayfs()) return ret;
+ if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) return ret;
if (!fstab) return ret;
@@ -761,7 +765,14 @@
return ret;
}
-std::vector<std::string> fs_mgr_overlayfs_required_devices(const fstab* fstab) {
+bool fs_mgr_overlayfs_mount_all(const std::vector<const fstab_rec*>& fsrecs) {
+ std::vector<fstab_rec> recs;
+ for (const auto& rec : fsrecs) recs.push_back(*rec);
+ fstab fstab = {static_cast<int>(fsrecs.size()), &recs[0]};
+ return fs_mgr_overlayfs_mount_all(&fstab);
+}
+
+std::vector<std::string> fs_mgr_overlayfs_required_devices(fstab* fstab) {
if (fs_mgr_get_entry_for_mount_point(const_cast<struct fstab*>(fstab), kScratchMountPoint)) {
return {};
}
@@ -775,12 +786,20 @@
return {};
}
+std::vector<std::string> fs_mgr_overlayfs_required_devices(
+ const std::vector<const fstab_rec*>& fsrecs) {
+ std::vector<fstab_rec> recs;
+ for (const auto& rec : fsrecs) recs.push_back(*rec);
+ fstab fstab = {static_cast<int>(fsrecs.size()), &recs[0]};
+ return fs_mgr_overlayfs_required_devices(&fstab);
+}
+
// Returns false if setup not permitted, errno set to last error.
// If something is altered, set *change.
bool fs_mgr_overlayfs_setup(const char* backing, const char* mount_point, bool* change) {
if (change) *change = false;
auto ret = false;
- if (!fs_mgr_wants_overlayfs()) return ret;
+ if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) return ret;
if (!fs_mgr_boot_completed()) {
errno = EBUSY;
PERROR << "setup";
@@ -843,7 +862,7 @@
for (const auto& overlay_mount_point : kOverlayMountPoints) {
ret &= fs_mgr_overlayfs_teardown_one(overlay_mount_point, mount_point ?: "", change);
}
- if (!fs_mgr_wants_overlayfs()) {
+ if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) {
// After obligatory teardown to make sure everything is clean, but if
// we didn't want overlayfs in the the first place, we do not want to
// waste time on a reboot (or reboot request message).
@@ -882,3 +901,42 @@
return (info.feat_ro_compat & EXT4_FEATURE_RO_COMPAT_SHARED_BLOCKS) != 0;
}
+
+std::string fs_mgr_get_context(const std::string& mount_point) {
+ char* ctx = nullptr;
+ if (getfilecon(mount_point.c_str(), &ctx) == -1) {
+ return "";
+ }
+
+ std::string context(ctx);
+ free(ctx);
+ return context;
+}
+
+OverlayfsValidResult fs_mgr_overlayfs_valid() {
+ // Overlayfs available in the kernel, and patched for override_creds?
+ if (fs_mgr_access("/sys/module/overlay/parameters/override_creds")) {
+ return OverlayfsValidResult::kOverrideCredsRequired;
+ }
+ if (!fs_mgr_access("/sys/module/overlay")) {
+ return OverlayfsValidResult::kNotSupported;
+ }
+ struct utsname uts;
+ if (uname(&uts) == -1) {
+ return OverlayfsValidResult::kNotSupported;
+ }
+ int major, minor;
+ if (sscanf(uts.release, "%d.%d", &major, &minor) != 2) {
+ return OverlayfsValidResult::kNotSupported;
+ }
+ if (major < 4) {
+ return OverlayfsValidResult::kOk;
+ }
+ if (major > 4) {
+ return OverlayfsValidResult::kNotSupported;
+ }
+ if (minor > 6) {
+ return OverlayfsValidResult::kNotSupported;
+ }
+ return OverlayfsValidResult::kOk;
+}
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 5e83cfb..23a92d3 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -39,11 +39,13 @@
#define LINFO LOG(INFO) << FS_MGR_TAG
#define LWARNING LOG(WARNING) << FS_MGR_TAG
#define LERROR LOG(ERROR) << FS_MGR_TAG
+#define LFATAL LOG(FATAL) << FS_MGR_TAG
// Logs a message with strerror(errno) at the end
#define PINFO PLOG(INFO) << FS_MGR_TAG
#define PWARNING PLOG(WARNING) << FS_MGR_TAG
#define PERROR PLOG(ERROR) << FS_MGR_TAG
+#define PFATAL PLOG(FATAL) << FS_MGR_TAG
#define CRYPTO_TMPFS_OPTIONS "size=512m,mode=0771,uid=1000,gid=1000"
diff --git a/fs_mgr/fs_mgr_priv_avb_ops.h b/fs_mgr/fs_mgr_priv_avb_ops.h
index d1ef2e9..44eb1da 100644
--- a/fs_mgr/fs_mgr_priv_avb_ops.h
+++ b/fs_mgr/fs_mgr_priv_avb_ops.h
@@ -46,8 +46,7 @@
//
class FsManagerAvbOps {
public:
- FsManagerAvbOps(const fstab& fstab);
- FsManagerAvbOps(std::map<std::string, std::string>&& by_name_symlink_map);
+ FsManagerAvbOps();
static FsManagerAvbOps* GetInstanceFromAvbOps(AvbOps* ops) {
return reinterpret_cast<FsManagerAvbOps*>(ops->user_data);
@@ -60,8 +59,6 @@
AvbSlotVerifyData** out_data);
private:
- void InitializeAvbOps();
-
AvbOps avb_ops_;
std::map<std::string, std::string> by_name_symlink_map_;
};
diff --git a/fs_mgr/fs_mgr_vendor_overlay.cpp b/fs_mgr/fs_mgr_vendor_overlay.cpp
new file mode 100644
index 0000000..830f0dd
--- /dev/null
+++ b/fs_mgr/fs_mgr_vendor_overlay.cpp
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dirent.h>
+#include <selinux/selinux.h>
+#include <sys/mount.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <fs_mgr_overlayfs.h>
+#include <fs_mgr_vendor_overlay.h>
+#include <fstab/fstab.h>
+
+#include "fs_mgr_priv.h"
+
+using namespace std::literals;
+
+namespace {
+
+// The order of the list means the priority to show the files in the directory.
+// The last one has the highest priority.
+const std::vector<const std::string> kVendorOverlaySourceDirs = {
+ "/system/vendor_overlay/",
+ "/product/vendor_overlay/",
+};
+const auto kVndkVersionPropertyName = "ro.vndk.version"s;
+const auto kVendorTopDir = "/vendor/"s;
+const auto kLowerdirOption = "lowerdir="s;
+
+std::vector<std::pair<std::string, std::string>> fs_mgr_get_vendor_overlay_dirs(
+ const std::string& vndk_version) {
+ std::vector<std::pair<std::string, std::string>> vendor_overlay_dirs;
+ for (const auto& vendor_overlay_source : kVendorOverlaySourceDirs) {
+ const auto overlay_top = vendor_overlay_source + vndk_version;
+ std::unique_ptr<DIR, decltype(&closedir)> vendor_overlay_top(opendir(overlay_top.c_str()),
+ closedir);
+ if (!vendor_overlay_top) continue;
+
+ // Vendor overlay root for current vendor version found!
+ LINFO << "vendor overlay root: " << overlay_top;
+
+ struct dirent* dp;
+ while ((dp = readdir(vendor_overlay_top.get())) != nullptr) {
+ if (dp->d_type != DT_DIR || dp->d_name[0] == '.') {
+ continue;
+ }
+ vendor_overlay_dirs.emplace_back(overlay_top, dp->d_name);
+ }
+ }
+ return vendor_overlay_dirs;
+}
+
+bool fs_mgr_vendor_overlay_mount(const std::pair<std::string, std::string>& mount_point) {
+ const auto [overlay_top, mount_dir] = mount_point;
+ const auto vendor_mount_point = kVendorTopDir + mount_dir;
+ LINFO << "vendor overlay mount on " << vendor_mount_point;
+
+ const auto target_context = fs_mgr_get_context(vendor_mount_point);
+ if (target_context.empty()) {
+ PERROR << " failed: cannot find the target vendor mount point";
+ return false;
+ }
+ const auto source_directory = overlay_top + "/" + mount_dir;
+ const auto source_context = fs_mgr_get_context(source_directory);
+ if (target_context != source_context) {
+ LERROR << " failed: source and target contexts do not match (source:" << source_context
+ << ", target:" << target_context << ")";
+ return false;
+ }
+
+ auto options = kLowerdirOption + source_directory + ":" + vendor_mount_point;
+ if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kOverrideCredsRequired) {
+ options += ",override_creds=off";
+ }
+ auto report = "__mount(source=overlay,target="s + vendor_mount_point + ",type=overlay," +
+ options + ")=";
+ auto ret = mount("overlay", vendor_mount_point.c_str(), "overlay", MS_RDONLY | MS_RELATIME,
+ options.c_str());
+ if (ret) {
+ PERROR << report << ret;
+ return false;
+ } else {
+ LINFO << report << ret;
+ return true;
+ }
+}
+
+} // namespace
+
+// Since the vendor overlay requires to know the version of the vendor partition,
+// it is not possible to mount vendor overlay at the first stage that cannot
+// initialize properties.
+// To read the properties, vendor overlay must be mounted at the second stage, right
+// after "property_load_boot_defaults()" is called.
+bool fs_mgr_vendor_overlay_mount_all() {
+ // To read the property, it must be called at the second init stage after the default
+ // properties are loaded.
+ static const auto vndk_version = android::base::GetProperty(kVndkVersionPropertyName, "");
+ if (vndk_version.empty()) {
+ LINFO << "vendor overlay: vndk version not defined";
+ return false;
+ }
+
+ const auto vendor_overlay_dirs = fs_mgr_get_vendor_overlay_dirs(vndk_version);
+ if (vendor_overlay_dirs.empty()) return true;
+ if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) {
+ LINFO << "vendor overlay: kernel does not support overlayfs";
+ return false;
+ }
+
+ // Mount each directory in /(system|product)/vendor_overlay/<ver> on /vendor
+ auto ret = true;
+ for (const auto& vendor_overlay_dir : vendor_overlay_dirs) {
+ if (!fs_mgr_vendor_overlay_mount(vendor_overlay_dir)) {
+ ret = false;
+ }
+ }
+ return ret;
+}
diff --git a/fs_mgr/include/fs_mgr_avb.h b/fs_mgr/include/fs_mgr_avb.h
index 73a22c8..bb55a14 100644
--- a/fs_mgr/include/fs_mgr_avb.h
+++ b/fs_mgr/include/fs_mgr_avb.h
@@ -53,13 +53,6 @@
// A typical usage will be:
// - FsManagerAvbUniquePtr handle = FsManagerAvbHandle::Open();
//
- // There are two overloaded Open() functions with a single parameter.
- // The argument can be a ByNameSymlinkMap describing the mapping from partition
- // name to by-name symlink, or a fstab file to which the ByNameSymlinkMap is
- // constructed from. e.g.,
- // - /dev/block/platform/soc.0/7824900.sdhci/by-name/system_a ->
- // - ByNameSymlinkMap["system_a"] = "/dev/block/platform/soc.0/7824900.sdhci/by-name/system_a"
- //
// Possible return values:
// - nullptr: any error when reading and verifying the metadata,
// e.g., I/O error, digest value mismatch, size mismatch, etc.
@@ -82,8 +75,7 @@
// - a valid unique_ptr with status kAvbHandleSuccess: the metadata
// is verified and can be trusted.
//
- static FsManagerAvbUniquePtr Open(const fstab& fstab);
- static FsManagerAvbUniquePtr Open(ByNameSymlinkMap&& by_name_symlink_map);
+ static FsManagerAvbUniquePtr Open();
// Sets up dm-verity on the given fstab entry.
// The 'wait_for_verity_dev' parameter makes this function wait for the
@@ -121,7 +113,6 @@
};
FsManagerAvbHandle() : avb_slot_data_(nullptr), status_(kAvbHandleUninitialized) {}
- static FsManagerAvbUniquePtr DoOpen(FsManagerAvbOps* avb_ops);
AvbSlotVerifyData* avb_slot_data_;
AvbHandleStatus status_;
diff --git a/fs_mgr/include/fs_mgr_dm_linear.h b/fs_mgr/include/fs_mgr_dm_linear.h
index 08f4554..66abfca 100644
--- a/fs_mgr/include/fs_mgr_dm_linear.h
+++ b/fs_mgr/include/fs_mgr_dm_linear.h
@@ -33,11 +33,20 @@
#include <vector>
#include <libdm/dm.h>
-#include <liblp/metadata_format.h>
+#include <liblp/liblp.h>
namespace android {
namespace fs_mgr {
+// Read metadata from the current slot.
+std::unique_ptr<LpMetadata> ReadCurrentMetadata(const std::string& block_device);
+
+// Create block devices for all logical partitions in the given metadata. The
+// metadata must have been read from the current slot.
+bool CreateLogicalPartitions(const LpMetadata& metadata);
+
+// Create block devices for all logical partitions. This is a convenience
+// method for ReadMetadata and CreateLogicalPartitions.
bool CreateLogicalPartitions(const std::string& block_device);
// Create a block device for a single logical partition, given metadata and
diff --git a/fs_mgr/include/fs_mgr_overlayfs.h b/fs_mgr/include/fs_mgr_overlayfs.h
index 3274e0e..deaf4cb 100644
--- a/fs_mgr/include/fs_mgr_overlayfs.h
+++ b/fs_mgr/include/fs_mgr_overlayfs.h
@@ -21,9 +21,20 @@
#include <string>
#include <vector>
-bool fs_mgr_overlayfs_mount_all(const fstab* fstab);
-std::vector<std::string> fs_mgr_overlayfs_required_devices(const fstab* fstab);
+bool fs_mgr_overlayfs_mount_all(fstab* fstab);
+bool fs_mgr_overlayfs_mount_all(const std::vector<const fstab_rec*>& fstab);
+std::vector<std::string> fs_mgr_overlayfs_required_devices(fstab* fstab);
+std::vector<std::string> fs_mgr_overlayfs_required_devices(
+ const std::vector<const fstab_rec*>& fstab);
bool fs_mgr_overlayfs_setup(const char* backing = nullptr, const char* mount_point = nullptr,
bool* change = nullptr);
bool fs_mgr_overlayfs_teardown(const char* mount_point = nullptr, bool* change = nullptr);
bool fs_mgr_has_shared_blocks(const std::string& mount_point, const std::string& dev);
+std::string fs_mgr_get_context(const std::string& mount_point);
+
+enum class OverlayfsValidResult {
+ kNotSupported = 0,
+ kOk,
+ kOverrideCredsRequired,
+};
+OverlayfsValidResult fs_mgr_overlayfs_valid();
diff --git a/fs_mgr/include/fs_mgr_vendor_overlay.h b/fs_mgr/include/fs_mgr_vendor_overlay.h
new file mode 100644
index 0000000..9771a0c
--- /dev/null
+++ b/fs_mgr/include/fs_mgr_vendor_overlay.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2018 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 <fstab/fstab.h>
+
+bool fs_mgr_vendor_overlay_mount_all();
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index 5689bdf..355b7a1 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -39,6 +39,11 @@
"libext4_utils",
"libz",
],
+ target: {
+ windows: {
+ enabled: true,
+ },
+ },
export_include_dirs: ["include"],
}
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 1b8ed57..da86d75 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -20,6 +20,7 @@
#include <algorithm>
+#include <android-base/properties.h>
#include <android-base/unique_fd.h>
#include "liblp/liblp.h"
@@ -29,12 +30,22 @@
namespace android {
namespace fs_mgr {
-void LinearExtent::AddTo(LpMetadata* out) const {
- out->extents.push_back(LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_LINEAR, physical_sector_});
+bool MetadataBuilder::sABOverrideSet;
+bool MetadataBuilder::sABOverrideValue;
+
+bool LinearExtent::AddTo(LpMetadata* out) const {
+ if (device_index_ >= out->block_devices.size()) {
+ LERROR << "Extent references unknown block device.";
+ return false;
+ }
+ out->extents.emplace_back(
+ LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_LINEAR, physical_sector_, device_index_});
+ return true;
}
-void ZeroExtent::AddTo(LpMetadata* out) const {
- out->extents.push_back(LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_ZERO, 0});
+bool ZeroExtent::AddTo(LpMetadata* out) const {
+ out->extents.emplace_back(LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_ZERO, 0, 0});
+ return true;
}
Partition::Partition(const std::string& name, const std::string& group_name, uint32_t attributes)
@@ -44,15 +55,17 @@
size_ += extent->num_sectors() * LP_SECTOR_SIZE;
if (LinearExtent* new_extent = extent->AsLinearExtent()) {
- if (!extents_.empty() && extents_.back()->AsLinearExtent() &&
- extents_.back()->AsLinearExtent()->end_sector() == new_extent->physical_sector()) {
- // If the previous extent can be merged into this new one, do so
- // to avoid creating unnecessary extents.
+ if (!extents_.empty() && extents_.back()->AsLinearExtent()) {
LinearExtent* prev_extent = extents_.back()->AsLinearExtent();
- extent = std::make_unique<LinearExtent>(
- prev_extent->num_sectors() + new_extent->num_sectors(),
- prev_extent->physical_sector());
- extents_.pop_back();
+ if (prev_extent->end_sector() == new_extent->physical_sector() &&
+ prev_extent->device_index() == new_extent->device_index()) {
+ // If the previous extent can be merged into this new one, do so
+ // to avoid creating unnecessary extents.
+ extent = std::make_unique<LinearExtent>(
+ prev_extent->num_sectors() + new_extent->num_sectors(),
+ prev_extent->device_index(), prev_extent->physical_sector());
+ extents_.pop_back();
+ }
}
}
extents_.push_back(std::move(extent));
@@ -104,15 +117,7 @@
if (!metadata) {
return nullptr;
}
- std::unique_ptr<MetadataBuilder> builder = New(*metadata.get());
- if (!builder) {
- return nullptr;
- }
- BlockDeviceInfo device_info;
- if (opener.GetInfo(super_partition, &device_info)) {
- builder->UpdateBlockDeviceInfo(device_info);
- }
- return builder;
+ return New(*metadata.get(), &opener);
}
std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const std::string& super_partition,
@@ -120,25 +125,94 @@
return New(PartitionOpener(), super_partition, slot_number);
}
-std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const BlockDeviceInfo& device_info,
- uint32_t metadata_max_size,
- uint32_t metadata_slot_count) {
+std::unique_ptr<MetadataBuilder> MetadataBuilder::New(
+ const std::vector<BlockDeviceInfo>& block_devices, const std::string& super_partition,
+ uint32_t metadata_max_size, uint32_t metadata_slot_count) {
std::unique_ptr<MetadataBuilder> builder(new MetadataBuilder());
- if (!builder->Init(device_info, metadata_max_size, metadata_slot_count)) {
+ if (!builder->Init(block_devices, super_partition, metadata_max_size, metadata_slot_count)) {
return nullptr;
}
return builder;
}
-std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const LpMetadata& metadata) {
+std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const LpMetadata& metadata,
+ const IPartitionOpener* opener) {
std::unique_ptr<MetadataBuilder> builder(new MetadataBuilder());
if (!builder->Init(metadata)) {
return nullptr;
}
+ if (opener) {
+ for (size_t i = 0; i < builder->block_devices_.size(); i++) {
+ std::string partition_name = GetBlockDevicePartitionName(builder->block_devices_[i]);
+ BlockDeviceInfo device_info;
+ if (opener->GetInfo(partition_name, &device_info)) {
+ builder->UpdateBlockDeviceInfo(i, device_info);
+ }
+ }
+ }
return builder;
}
-MetadataBuilder::MetadataBuilder() {
+std::unique_ptr<MetadataBuilder> MetadataBuilder::NewForUpdate(const IPartitionOpener& opener,
+ const std::string& source_partition,
+ uint32_t source_slot_number,
+ uint32_t target_slot_number) {
+ auto metadata = ReadMetadata(opener, source_partition, source_slot_number);
+ if (!metadata) {
+ return nullptr;
+ }
+
+ // On non-retrofit devices there is only one location for metadata: the
+ // super partition. update_engine will remove and resize partitions as
+ // needed. On the other hand, for retrofit devices, we'll need to
+ // translate block device and group names to update their slot suffixes.
+ auto super_device = GetMetadataSuperBlockDevice(*metadata.get());
+ if (GetBlockDevicePartitionName(*super_device) == "super") {
+ return New(*metadata.get(), &opener);
+ }
+
+ // Clear partitions and extents, since they have no meaning on the target
+ // slot. We also clear groups since they are re-added during OTA.
+ metadata->partitions.clear();
+ metadata->extents.clear();
+ metadata->groups.clear();
+
+ std::string source_slot_suffix = SlotSuffixForSlotNumber(source_slot_number);
+ std::string target_slot_suffix = SlotSuffixForSlotNumber(target_slot_number);
+
+ // Translate block devices.
+ auto source_block_devices = std::move(metadata->block_devices);
+ for (const auto& source_block_device : source_block_devices) {
+ std::string partition_name = GetBlockDevicePartitionName(source_block_device);
+ std::string slot_suffix = GetPartitionSlotSuffix(partition_name);
+ if (slot_suffix.empty() || slot_suffix != source_slot_suffix) {
+ // This should never happen. It means that the source metadata
+ // refers to a target or unknown block device.
+ LERROR << "Invalid block device for slot " << source_slot_suffix << ": "
+ << partition_name;
+ return nullptr;
+ }
+ std::string new_name =
+ partition_name.substr(0, partition_name.size() - slot_suffix.size()) +
+ target_slot_suffix;
+
+ auto new_device = source_block_device;
+ if (!UpdateBlockDevicePartitionName(&new_device, new_name)) {
+ LERROR << "Partition name too long: " << new_name;
+ return nullptr;
+ }
+ metadata->block_devices.emplace_back(new_device);
+ }
+
+ return New(*metadata.get(), &opener);
+}
+
+void MetadataBuilder::OverrideABForTesting(bool ab_device) {
+ sABOverrideSet = true;
+ sABOverrideValue = ab_device;
+}
+
+MetadataBuilder::MetadataBuilder() : auto_slot_suffixing_(false) {
memset(&geometry_, 0, sizeof(geometry_));
geometry_.magic = LP_METADATA_GEOMETRY_MAGIC;
geometry_.struct_size = sizeof(geometry_);
@@ -156,6 +230,7 @@
bool MetadataBuilder::Init(const LpMetadata& metadata) {
geometry_ = metadata.geometry;
+ block_devices_ = metadata.block_devices;
for (const auto& group : metadata.groups) {
std::string group_name = GetPartitionGroupName(group);
@@ -164,10 +239,6 @@
}
}
- for (const auto& block_device : metadata.block_devices) {
- block_devices_.push_back(block_device);
- }
-
for (const auto& partition : metadata.partitions) {
std::string group_name = GetPartitionGroupName(metadata.groups[partition.group_index]);
Partition* builder =
@@ -175,22 +246,57 @@
if (!builder) {
return false;
}
-
- for (size_t i = 0; i < partition.num_extents; i++) {
- const LpMetadataExtent& extent = metadata.extents[partition.first_extent_index + i];
- if (extent.target_type == LP_TARGET_TYPE_LINEAR) {
- auto copy = std::make_unique<LinearExtent>(extent.num_sectors, extent.target_data);
- builder->AddExtent(std::move(copy));
- } else if (extent.target_type == LP_TARGET_TYPE_ZERO) {
- auto copy = std::make_unique<ZeroExtent>(extent.num_sectors);
- builder->AddExtent(std::move(copy));
- }
- }
+ ImportExtents(builder, metadata, partition);
}
return true;
}
-bool MetadataBuilder::Init(const BlockDeviceInfo& device_info, uint32_t metadata_max_size,
+void MetadataBuilder::ImportExtents(Partition* dest, const LpMetadata& metadata,
+ const LpMetadataPartition& source) {
+ for (size_t i = 0; i < source.num_extents; i++) {
+ const LpMetadataExtent& extent = metadata.extents[source.first_extent_index + i];
+ if (extent.target_type == LP_TARGET_TYPE_LINEAR) {
+ auto copy = std::make_unique<LinearExtent>(extent.num_sectors, extent.target_source,
+ extent.target_data);
+ dest->AddExtent(std::move(copy));
+ } else if (extent.target_type == LP_TARGET_TYPE_ZERO) {
+ auto copy = std::make_unique<ZeroExtent>(extent.num_sectors);
+ dest->AddExtent(std::move(copy));
+ }
+ }
+}
+
+static bool VerifyDeviceProperties(const BlockDeviceInfo& device_info) {
+ if (device_info.logical_block_size % LP_SECTOR_SIZE != 0) {
+ LERROR << "Block device " << device_info.partition_name
+ << " logical block size must be a multiple of 512.";
+ return false;
+ }
+ if (device_info.size % device_info.logical_block_size != 0) {
+ LERROR << "Block device " << device_info.partition_name
+ << " size must be a multiple of its block size.";
+ return false;
+ }
+ if (device_info.alignment_offset % LP_SECTOR_SIZE != 0) {
+ LERROR << "Block device " << device_info.partition_name
+ << " alignment offset is not sector-aligned.";
+ return false;
+ }
+ if (device_info.alignment % LP_SECTOR_SIZE != 0) {
+ LERROR << "Block device " << device_info.partition_name
+ << " partition alignment is not sector-aligned.";
+ return false;
+ }
+ if (device_info.alignment_offset > device_info.alignment) {
+ LERROR << "Block device " << device_info.partition_name
+ << " partition alignment offset is greater than its alignment.";
+ return false;
+ }
+ return true;
+}
+
+bool MetadataBuilder::Init(const std::vector<BlockDeviceInfo>& block_devices,
+ const std::string& super_partition, uint32_t metadata_max_size,
uint32_t metadata_slot_count) {
if (metadata_max_size < sizeof(LpMetadataHeader)) {
LERROR << "Invalid metadata maximum size.";
@@ -200,70 +306,102 @@
LERROR << "Invalid metadata slot count.";
return false;
}
+ if (block_devices.empty()) {
+ LERROR << "No block devices were specified.";
+ return false;
+ }
// Align the metadata size up to the nearest sector.
metadata_max_size = AlignTo(metadata_max_size, LP_SECTOR_SIZE);
- // Check that device properties are sane.
- if (device_info.size % LP_SECTOR_SIZE != 0) {
- LERROR << "Block device size must be a multiple of 512.";
+ // Validate and build the block device list.
+ uint32_t logical_block_size = 0;
+ for (const auto& device_info : block_devices) {
+ if (!VerifyDeviceProperties(device_info)) {
+ return false;
+ }
+
+ if (!logical_block_size) {
+ logical_block_size = device_info.logical_block_size;
+ }
+ if (logical_block_size != device_info.logical_block_size) {
+ LERROR << "All partitions must have the same logical block size.";
+ return false;
+ }
+
+ LpMetadataBlockDevice out = {};
+ out.alignment = device_info.alignment;
+ out.alignment_offset = device_info.alignment_offset;
+ out.size = device_info.size;
+ if (device_info.partition_name.size() >= sizeof(out.partition_name)) {
+ LERROR << "Partition name " << device_info.partition_name << " exceeds maximum length.";
+ return false;
+ }
+ strncpy(out.partition_name, device_info.partition_name.c_str(), sizeof(out.partition_name));
+
+ // In the case of the super partition, this field will be adjusted
+ // later. For all partitions, the first 512 bytes are considered
+ // untouched to be compatible code that looks for an MBR. Thus we
+ // start counting free sectors at sector 1, not 0.
+ uint64_t free_area_start = LP_SECTOR_SIZE;
+ if (out.alignment || out.alignment_offset) {
+ free_area_start = AlignTo(free_area_start, out.alignment, out.alignment_offset);
+ } else {
+ free_area_start = AlignTo(free_area_start, logical_block_size);
+ }
+ out.first_logical_sector = free_area_start / LP_SECTOR_SIZE;
+
+ // There must be one logical block of space available.
+ uint64_t minimum_size = out.first_logical_sector * LP_SECTOR_SIZE + logical_block_size;
+ if (device_info.size < minimum_size) {
+ LERROR << "Block device " << device_info.partition_name
+ << " is too small to hold any logical partitions.";
+ return false;
+ }
+
+ // The "root" of the super partition is always listed first.
+ if (device_info.partition_name == super_partition) {
+ block_devices_.emplace(block_devices_.begin(), out);
+ } else {
+ block_devices_.emplace_back(out);
+ }
+ }
+ if (GetBlockDevicePartitionName(block_devices_[0]) != super_partition) {
+ LERROR << "No super partition was specified.";
return false;
}
- if (device_info.logical_block_size % LP_SECTOR_SIZE != 0) {
- LERROR << "Logical block size must be a multiple of 512.";
- return false;
- }
- if (device_info.alignment_offset % LP_SECTOR_SIZE != 0) {
- LERROR << "Alignment offset is not sector-aligned.";
- return false;
- }
- if (device_info.alignment % LP_SECTOR_SIZE != 0) {
- LERROR << "Partition alignment is not sector-aligned.";
- return false;
- }
- if (device_info.alignment_offset > device_info.alignment) {
- LERROR << "Partition alignment offset is greater than its alignment.";
- return false;
- }
+
+ LpMetadataBlockDevice& super = block_devices_[0];
// We reserve a geometry block (4KB) plus space for each copy of the
// maximum size of a metadata blob. Then, we double that space since
// we store a backup copy of everything.
uint64_t total_reserved = GetTotalMetadataSize(metadata_max_size, metadata_slot_count);
- if (device_info.size < total_reserved) {
+ if (super.size < total_reserved) {
LERROR << "Attempting to create metadata on a block device that is too small.";
return false;
}
// Compute the first free sector, factoring in alignment.
uint64_t free_area_start = total_reserved;
- if (device_info.alignment || device_info.alignment_offset) {
- free_area_start =
- AlignTo(free_area_start, device_info.alignment, device_info.alignment_offset);
+ if (super.alignment || super.alignment_offset) {
+ free_area_start = AlignTo(free_area_start, super.alignment, super.alignment_offset);
} else {
- free_area_start = AlignTo(free_area_start, device_info.logical_block_size);
+ free_area_start = AlignTo(free_area_start, logical_block_size);
}
- uint64_t first_sector = free_area_start / LP_SECTOR_SIZE;
+ super.first_logical_sector = free_area_start / LP_SECTOR_SIZE;
// There must be one logical block of free space remaining (enough for one partition).
- uint64_t minimum_disk_size = (first_sector * LP_SECTOR_SIZE) + device_info.logical_block_size;
- if (device_info.size < minimum_disk_size) {
+ uint64_t minimum_disk_size = (super.first_logical_sector * LP_SECTOR_SIZE) + logical_block_size;
+ if (super.size < minimum_disk_size) {
LERROR << "Device must be at least " << minimum_disk_size << " bytes, only has "
- << device_info.size;
+ << super.size;
return false;
}
- block_devices_.push_back(LpMetadataBlockDevice{
- first_sector,
- device_info.alignment,
- device_info.alignment_offset,
- device_info.size,
- "super",
- });
-
geometry_.metadata_max_size = metadata_max_size;
geometry_.metadata_slot_count = metadata_slot_count;
- geometry_.logical_block_size = device_info.logical_block_size;
+ geometry_.logical_block_size = logical_block_size;
if (!AddGroup("default", 0)) {
return false;
@@ -298,6 +436,11 @@
LERROR << "Could not find partition group: " << group_name;
return nullptr;
}
+ if (IsABDevice() && !auto_slot_suffixing_ && name != "scratch" &&
+ GetPartitionSlotSuffix(name).empty()) {
+ LERROR << "Unsuffixed partition not allowed on A/B device: " << name;
+ return nullptr;
+ }
partitions_.push_back(std::make_unique<Partition>(name, group_name, attributes));
return partitions_.back().get();
}
@@ -347,8 +490,9 @@
for (size_t i = 1; i < extents.size(); i++) {
const Interval& previous = extents[i - 1];
const Interval& current = extents[i];
+ DCHECK(previous.device_index == current.device_index);
- uint64_t aligned = AlignSector(previous.end);
+ uint64_t aligned = AlignSector(block_devices_[current.device_index], previous.end);
if (aligned >= current.start) {
// There is no gap between these two extents, try the next one.
// Note that we check with >= instead of >, since alignment may
@@ -358,58 +502,73 @@
// The new interval represents the free space starting at the end of
// the previous interval, and ending at the start of the next interval.
- free_regions->emplace_back(aligned, current.start);
+ free_regions->emplace_back(current.device_index, aligned, current.start);
}
}
auto MetadataBuilder::GetFreeRegions() const -> std::vector<Interval> {
std::vector<Interval> free_regions;
- // Collect all extents in the partition table, then sort them by starting
- // sector.
- std::vector<Interval> extents;
+ // Collect all extents in the partition table, per-device, then sort them
+ // by starting sector.
+ std::vector<std::vector<Interval>> device_extents(block_devices_.size());
for (const auto& partition : partitions_) {
for (const auto& extent : partition->extents()) {
LinearExtent* linear = extent->AsLinearExtent();
if (!linear) {
continue;
}
- extents.emplace_back(linear->physical_sector(),
+ CHECK(linear->device_index() < device_extents.size());
+ auto& extents = device_extents[linear->device_index()];
+ extents.emplace_back(linear->device_index(), linear->physical_sector(),
linear->physical_sector() + extent->num_sectors());
}
}
// Add 0-length intervals for the first and last sectors. This will cause
// ExtentToFreeList() to treat the space in between as available.
- uint64_t first_sector = super_device().first_logical_sector;
- uint64_t last_sector = super_device().size / LP_SECTOR_SIZE;
- extents.emplace_back(first_sector, first_sector);
- extents.emplace_back(last_sector, last_sector);
+ for (size_t i = 0; i < device_extents.size(); i++) {
+ auto& extents = device_extents[i];
+ const auto& block_device = block_devices_[i];
- std::sort(extents.begin(), extents.end());
+ uint64_t first_sector = block_device.first_logical_sector;
+ uint64_t last_sector = block_device.size / LP_SECTOR_SIZE;
+ extents.emplace_back(i, first_sector, first_sector);
+ extents.emplace_back(i, last_sector, last_sector);
- ExtentsToFreeList(extents, &free_regions);
+ std::sort(extents.begin(), extents.end());
+ ExtentsToFreeList(extents, &free_regions);
+ }
return free_regions;
}
-bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t aligned_size) {
+bool MetadataBuilder::ValidatePartitionSizeChange(Partition* partition, uint64_t old_size,
+ uint64_t new_size) {
PartitionGroup* group = FindGroup(partition->group_name());
CHECK(group);
+ if (new_size <= old_size) {
+ return true;
+ }
+
// Figure out how much we need to allocate, and whether our group has
// enough space remaining.
- uint64_t space_needed = aligned_size - partition->size();
+ uint64_t space_needed = new_size - old_size;
if (group->maximum_size() > 0) {
uint64_t group_size = TotalSizeOfGroup(group);
if (group_size >= group->maximum_size() ||
group->maximum_size() - group_size < space_needed) {
LERROR << "Partition " << partition->name() << " is part of group " << group->name()
- << " which does not have enough space free (" << space_needed << "requested, "
- << group_size << " used out of " << group->maximum_size();
+ << " which does not have enough space free (" << space_needed << " requested, "
+ << group_size << " used out of " << group->maximum_size() << ")";
return false;
}
}
+ return true;
+}
+bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t aligned_size) {
+ uint64_t space_needed = aligned_size - partition->size();
uint64_t sectors_needed = space_needed / LP_SECTOR_SIZE;
DCHECK(sectors_needed * LP_SECTOR_SIZE == space_needed);
@@ -443,7 +602,7 @@
uint64_t sectors = std::min(sectors_needed, region.length());
CHECK(sectors % sectors_per_block == 0);
- auto extent = std::make_unique<LinearExtent>(sectors, region.start);
+ auto extent = std::make_unique<LinearExtent>(sectors, region.device_index, region.start);
new_extents.push_back(std::move(extent));
sectors_needed -= sectors;
if (!sectors_needed) {
@@ -471,6 +630,14 @@
metadata->header = header_;
metadata->geometry = geometry_;
+ // Assign this early so the extent table can read it.
+ for (const auto& block_device : block_devices_) {
+ metadata->block_devices.emplace_back(block_device);
+ if (auto_slot_suffixing_) {
+ metadata->block_devices.back().flags |= LP_BLOCK_DEVICE_SLOT_SUFFIXED;
+ }
+ }
+
std::map<std::string, size_t> group_indices;
for (const auto& group : groups_) {
LpMetadataPartitionGroup out = {};
@@ -479,6 +646,9 @@
LERROR << "Partition group name is too long: " << group->name();
return nullptr;
}
+ if (auto_slot_suffixing_ && group->name() != "default") {
+ out.flags |= LP_GROUP_SLOT_SUFFIXED;
+ }
strncpy(out.name, group->name().c_str(), sizeof(out.name));
out.maximum_size = group->maximum_size();
@@ -505,6 +675,9 @@
part.first_extent_index = static_cast<uint32_t>(metadata->extents.size());
part.num_extents = static_cast<uint32_t>(partition->extents().size());
part.attributes = partition->attributes();
+ if (auto_slot_suffixing_) {
+ part.attributes |= LP_PARTITION_ATTR_SLOT_SUFFIXED;
+ }
auto iter = group_indices.find(partition->group_name());
if (iter == group_indices.end()) {
@@ -515,13 +688,13 @@
part.group_index = iter->second;
for (const auto& extent : partition->extents()) {
- extent->AddTo(metadata.get());
+ if (!extent->AddTo(metadata.get())) {
+ return nullptr;
+ }
}
metadata->partitions.push_back(part);
}
- metadata->block_devices = block_devices_;
-
metadata->header.partitions.num_entries = static_cast<uint32_t>(metadata->partitions.size());
metadata->header.extents.num_entries = static_cast<uint32_t>(metadata->extents.size());
metadata->header.groups.num_entries = static_cast<uint32_t>(metadata->groups.size());
@@ -531,7 +704,11 @@
}
uint64_t MetadataBuilder::AllocatableSpace() const {
- return super_device().size - (super_device().first_logical_sector * LP_SECTOR_SIZE);
+ uint64_t total_size = 0;
+ for (const auto& block_device : block_devices_) {
+ total_size += block_device.size - (block_device.first_logical_sector * LP_SECTOR_SIZE);
+ }
+ return total_size;
}
uint64_t MetadataBuilder::UsedSpace() const {
@@ -542,26 +719,63 @@
return size;
}
-uint64_t MetadataBuilder::AlignSector(uint64_t sector) const {
+uint64_t MetadataBuilder::AlignSector(const LpMetadataBlockDevice& block_device,
+ uint64_t sector) const {
// Note: when reading alignment info from the Kernel, we don't assume it
// is aligned to the sector size, so we round up to the nearest sector.
uint64_t lba = sector * LP_SECTOR_SIZE;
- uint64_t aligned = AlignTo(lba, super_device().alignment, super_device().alignment_offset);
+ uint64_t aligned = AlignTo(lba, block_device.alignment, block_device.alignment_offset);
return AlignTo(aligned, LP_SECTOR_SIZE) / LP_SECTOR_SIZE;
}
-bool MetadataBuilder::GetBlockDeviceInfo(BlockDeviceInfo* info) const {
- info->size = super_device().size;
- info->alignment = super_device().alignment;
- info->alignment_offset = super_device().alignment_offset;
+bool MetadataBuilder::FindBlockDeviceByName(const std::string& partition_name,
+ uint32_t* index) const {
+ for (size_t i = 0; i < block_devices_.size(); i++) {
+ if (GetBlockDevicePartitionName(block_devices_[i]) == partition_name) {
+ *index = i;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool MetadataBuilder::HasBlockDevice(const std::string& partition_name) const {
+ uint32_t index;
+ return FindBlockDeviceByName(partition_name, &index);
+}
+
+bool MetadataBuilder::GetBlockDeviceInfo(const std::string& partition_name,
+ BlockDeviceInfo* info) const {
+ uint32_t index;
+ if (!FindBlockDeviceByName(partition_name, &index)) {
+ LERROR << "No device named " << partition_name;
+ return false;
+ }
+ info->size = block_devices_[index].size;
+ info->alignment = block_devices_[index].alignment;
+ info->alignment_offset = block_devices_[index].alignment_offset;
info->logical_block_size = geometry_.logical_block_size;
+ info->partition_name = partition_name;
return true;
}
-bool MetadataBuilder::UpdateBlockDeviceInfo(const BlockDeviceInfo& device_info) {
- if (device_info.size != super_device().size) {
+bool MetadataBuilder::UpdateBlockDeviceInfo(const std::string& partition_name,
+ const BlockDeviceInfo& device_info) {
+ uint32_t index;
+ if (!FindBlockDeviceByName(partition_name, &index)) {
+ LERROR << "No device named " << partition_name;
+ return false;
+ }
+ return UpdateBlockDeviceInfo(index, device_info);
+}
+
+bool MetadataBuilder::UpdateBlockDeviceInfo(size_t index, const BlockDeviceInfo& device_info) {
+ CHECK(index < block_devices_.size());
+
+ LpMetadataBlockDevice& block_device = block_devices_[index];
+ if (device_info.size != block_device.size) {
LERROR << "Device size does not match (got " << device_info.size << ", expected "
- << super_device().size << ")";
+ << block_device.size << ")";
return false;
}
if (device_info.logical_block_size != geometry_.logical_block_size) {
@@ -573,10 +787,10 @@
// The kernel does not guarantee these values are present, so we only
// replace existing values if the new values are non-zero.
if (device_info.alignment) {
- super_device().alignment = device_info.alignment;
+ block_device.alignment = device_info.alignment;
}
if (device_info.alignment_offset) {
- super_device().alignment_offset = device_info.alignment_offset;
+ block_device.alignment_offset = device_info.alignment_offset;
}
return true;
}
@@ -586,6 +800,10 @@
uint64_t aligned_size = AlignTo(requested_size, geometry_.logical_block_size);
uint64_t old_size = partition->size();
+ if (!ValidatePartitionSizeChange(partition, old_size, aligned_size)) {
+ return false;
+ }
+
if (aligned_size > old_size) {
if (!GrowPartition(partition, aligned_size)) {
return false;
@@ -632,5 +850,85 @@
}
}
+static bool CompareBlockDevices(const LpMetadataBlockDevice& first,
+ const LpMetadataBlockDevice& second) {
+ // Note: we don't compare alignment, since it's a performance thing and
+ // won't affect whether old extents continue to work.
+ return first.first_logical_sector == second.first_logical_sector && first.size == second.size &&
+ GetBlockDevicePartitionName(first) == GetBlockDevicePartitionName(second);
+}
+
+bool MetadataBuilder::ImportPartitions(const LpMetadata& metadata,
+ const std::set<std::string>& partition_names) {
+ // The block device list must be identical. We do not try to be clever and
+ // allow ordering changes or changes that don't affect partitions. This
+ // process is designed to allow the most common flashing scenarios and more
+ // complex ones should require a wipe.
+ if (metadata.block_devices.size() != block_devices_.size()) {
+ LINFO << "Block device tables does not match.";
+ return false;
+ }
+ for (size_t i = 0; i < metadata.block_devices.size(); i++) {
+ const LpMetadataBlockDevice& old_device = metadata.block_devices[i];
+ const LpMetadataBlockDevice& new_device = block_devices_[i];
+ if (!CompareBlockDevices(old_device, new_device)) {
+ LINFO << "Block device tables do not match";
+ return false;
+ }
+ }
+
+ // Import named partitions. Note that we do not attempt to merge group
+ // information here. If the device changed its group names, the old
+ // partitions will fail to merge. The same could happen if the group
+ // allocation sizes change.
+ for (const auto& partition : metadata.partitions) {
+ std::string partition_name = GetPartitionName(partition);
+ if (partition_names.find(partition_name) == partition_names.end()) {
+ continue;
+ }
+ if (!ImportPartition(metadata, partition)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool MetadataBuilder::ImportPartition(const LpMetadata& metadata,
+ const LpMetadataPartition& source) {
+ std::string partition_name = GetPartitionName(source);
+ Partition* partition = FindPartition(partition_name);
+ if (!partition) {
+ std::string group_name = GetPartitionGroupName(metadata.groups[source.group_index]);
+ partition = AddPartition(partition_name, group_name, source.attributes);
+ if (!partition) {
+ return false;
+ }
+ }
+ if (partition->size() > 0) {
+ LINFO << "Importing partition table would overwrite non-empty partition: "
+ << partition_name;
+ return false;
+ }
+
+ ImportExtents(partition, metadata, source);
+
+ if (!ValidatePartitionSizeChange(partition, 0, partition->size())) {
+ partition->RemoveExtents();
+ return false;
+ }
+ return true;
+}
+
+void MetadataBuilder::SetAutoSlotSuffixing() {
+ auto_slot_suffixing_ = true;
+}
+
+bool MetadataBuilder::IsABDevice() const {
+ if (sABOverrideSet) {
+ return sABOverrideValue;
+ }
+ return android::base::GetBoolProperty("ro.build.ab_update", false);
+}
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index c02242a..926fe12 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -25,8 +25,27 @@
using namespace android::fs_mgr;
using ::testing::ElementsAre;
-TEST(liblp, BuildBasic) {
+class Environment : public ::testing::Environment {
+ public:
+ void SetUp() override { MetadataBuilder::OverrideABForTesting(false); }
+};
+
+int main(int argc, char** argv) {
+ std::unique_ptr<Environment> env(new Environment);
+ ::testing::AddGlobalTestEnvironment(env.get());
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
+
+class BuilderTest : public ::testing::Test {
+ public:
+ void SetUp() override { MetadataBuilder::OverrideABForTesting(false); }
+ void TearDown() override { MetadataBuilder::OverrideABForTesting(false); }
+};
+
+TEST_F(BuilderTest, BuildBasic) {
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+ ASSERT_NE(builder, nullptr);
Partition* partition = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
ASSERT_NE(partition, nullptr);
@@ -39,8 +58,9 @@
EXPECT_EQ(builder->FindPartition("system"), nullptr);
}
-TEST(liblp, ResizePartition) {
+TEST_F(BuilderTest, ResizePartition) {
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+ ASSERT_NE(builder, nullptr);
Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
ASSERT_NE(system, nullptr);
@@ -92,8 +112,9 @@
EXPECT_EQ(system->extents().size(), 0);
}
-TEST(liblp, PartitionAlignment) {
+TEST_F(BuilderTest, PartitionAlignment) {
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+ ASSERT_NE(builder, nullptr);
// Test that we align up to one sector.
Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
@@ -107,7 +128,7 @@
EXPECT_EQ(system->extents().size(), 1);
}
-TEST(liblp, DiskAlignment) {
+TEST_F(BuilderTest, DiskAlignment) {
static const uint64_t kDiskSize = 1000000;
static const uint32_t kMetadataSize = 1024;
static const uint32_t kMetadataSlots = 2;
@@ -117,17 +138,18 @@
ASSERT_EQ(builder, nullptr);
}
-TEST(liblp, MetadataAlignment) {
+TEST_F(BuilderTest, MetadataAlignment) {
// Make sure metadata sizes get aligned up.
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1000, 2);
+ ASSERT_NE(builder, nullptr);
unique_ptr<LpMetadata> exported = builder->Export();
ASSERT_NE(exported, nullptr);
EXPECT_EQ(exported->geometry.metadata_max_size, 1024);
}
-TEST(liblp, InternalAlignment) {
+TEST_F(BuilderTest, InternalAlignment) {
// Test the metadata fitting within alignment.
- BlockDeviceInfo device_info(1024 * 1024, 768 * 1024, 0, 4096);
+ BlockDeviceInfo device_info("super", 1024 * 1024, 768 * 1024, 0, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 2);
ASSERT_NE(builder, nullptr);
unique_ptr<LpMetadata> exported = builder->Export();
@@ -173,8 +195,8 @@
EXPECT_EQ(super_device->first_logical_sector, 160);
}
-TEST(liblp, InternalPartitionAlignment) {
- BlockDeviceInfo device_info(512 * 1024 * 1024, 768 * 1024, 753664, 4096);
+TEST_F(BuilderTest, InternalPartitionAlignment) {
+ BlockDeviceInfo device_info("super", 512 * 1024 * 1024, 768 * 1024, 753664, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 32 * 1024, 2);
Partition* a = builder->AddPartition("a", 0);
@@ -207,7 +229,7 @@
EXPECT_EQ(exported->extents.back().target_data, 30656);
}
-TEST(liblp, UseAllDiskSpace) {
+TEST_F(BuilderTest, UseAllDiskSpace) {
static constexpr uint64_t total = 1024 * 1024;
static constexpr uint64_t metadata = 1024;
static constexpr uint64_t slots = 2;
@@ -233,7 +255,7 @@
EXPECT_EQ(builder->AllocatableSpace(), allocatable);
}
-TEST(liblp, BuildComplex) {
+TEST_F(BuilderTest, BuildComplex) {
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
@@ -267,7 +289,7 @@
EXPECT_EQ(vendor1->physical_sector() + vendor1->num_sectors(), system2->physical_sector());
}
-TEST(liblp, AddInvalidPartition) {
+TEST_F(BuilderTest, AddInvalidPartition) {
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
Partition* partition = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
@@ -282,7 +304,7 @@
EXPECT_EQ(partition, nullptr);
}
-TEST(liblp, BuilderExport) {
+TEST_F(BuilderTest, BuilderExport) {
static const uint64_t kDiskSize = 1024 * 1024;
static const uint32_t kMetadataSize = 1024;
static const uint32_t kMetadataSlots = 2;
@@ -340,7 +362,7 @@
}
}
-TEST(liblp, BuilderImport) {
+TEST_F(BuilderTest, BuilderImport) {
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
@@ -378,7 +400,7 @@
EXPECT_EQ(vendor1->num_sectors(), 32768 / LP_SECTOR_SIZE);
}
-TEST(liblp, ExportNameTooLong) {
+TEST_F(BuilderTest, ExportNameTooLong) {
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
std::string name = "abcdefghijklmnopqrstuvwxyz0123456789";
@@ -389,12 +411,12 @@
EXPECT_EQ(exported, nullptr);
}
-TEST(liblp, MetadataTooLarge) {
+TEST_F(BuilderTest, MetadataTooLarge) {
static const size_t kDiskSize = 128 * 1024;
static const size_t kMetadataSize = 64 * 1024;
// No space to store metadata + geometry.
- BlockDeviceInfo device_info(kDiskSize, 0, 0, 4096);
+ BlockDeviceInfo device_info("super", kDiskSize, 0, 0, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
EXPECT_EQ(builder, nullptr);
@@ -419,7 +441,7 @@
EXPECT_EQ(builder, nullptr);
}
-TEST(liblp, block_device_info) {
+TEST_F(BuilderTest, block_device_info) {
std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
fs_mgr_free_fstab);
ASSERT_NE(fstab, nullptr);
@@ -440,13 +462,13 @@
ASSERT_LT(device_info.alignment_offset, device_info.alignment);
}
-TEST(liblp, UpdateBlockDeviceInfo) {
- BlockDeviceInfo device_info(1024 * 1024, 4096, 1024, 4096);
+TEST_F(BuilderTest, UpdateBlockDeviceInfo) {
+ BlockDeviceInfo device_info("super", 1024 * 1024, 4096, 1024, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
ASSERT_NE(builder, nullptr);
BlockDeviceInfo new_info;
- ASSERT_TRUE(builder->GetBlockDeviceInfo(&new_info));
+ ASSERT_TRUE(builder->GetBlockDeviceInfo("super", &new_info));
EXPECT_EQ(new_info.size, device_info.size);
EXPECT_EQ(new_info.alignment, device_info.alignment);
@@ -455,37 +477,37 @@
device_info.alignment = 0;
device_info.alignment_offset = 2048;
- ASSERT_TRUE(builder->UpdateBlockDeviceInfo(device_info));
- ASSERT_TRUE(builder->GetBlockDeviceInfo(&new_info));
+ ASSERT_TRUE(builder->UpdateBlockDeviceInfo("super", device_info));
+ ASSERT_TRUE(builder->GetBlockDeviceInfo("super", &new_info));
EXPECT_EQ(new_info.alignment, 4096);
EXPECT_EQ(new_info.alignment_offset, device_info.alignment_offset);
device_info.alignment = 8192;
device_info.alignment_offset = 0;
- ASSERT_TRUE(builder->UpdateBlockDeviceInfo(device_info));
- ASSERT_TRUE(builder->GetBlockDeviceInfo(&new_info));
+ ASSERT_TRUE(builder->UpdateBlockDeviceInfo("super", device_info));
+ ASSERT_TRUE(builder->GetBlockDeviceInfo("super", &new_info));
EXPECT_EQ(new_info.alignment, 8192);
EXPECT_EQ(new_info.alignment_offset, 2048);
new_info.size += 4096;
- ASSERT_FALSE(builder->UpdateBlockDeviceInfo(new_info));
- ASSERT_TRUE(builder->GetBlockDeviceInfo(&new_info));
+ ASSERT_FALSE(builder->UpdateBlockDeviceInfo("super", new_info));
+ ASSERT_TRUE(builder->GetBlockDeviceInfo("super", &new_info));
EXPECT_EQ(new_info.size, 1024 * 1024);
new_info.logical_block_size = 512;
- ASSERT_FALSE(builder->UpdateBlockDeviceInfo(new_info));
- ASSERT_TRUE(builder->GetBlockDeviceInfo(&new_info));
+ ASSERT_FALSE(builder->UpdateBlockDeviceInfo("super", new_info));
+ ASSERT_TRUE(builder->GetBlockDeviceInfo("super", &new_info));
EXPECT_EQ(new_info.logical_block_size, 4096);
}
-TEST(liblp, InvalidBlockSize) {
- BlockDeviceInfo device_info(1024 * 1024, 0, 0, 513);
+TEST_F(BuilderTest, InvalidBlockSize) {
+ BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 513);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
EXPECT_EQ(builder, nullptr);
}
-TEST(liblp, AlignedExtentSize) {
- BlockDeviceInfo device_info(1024 * 1024, 0, 0, 4096);
+TEST_F(BuilderTest, AlignedExtentSize) {
+ BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
ASSERT_NE(builder, nullptr);
@@ -495,23 +517,23 @@
EXPECT_EQ(partition->size(), 4096);
}
-TEST(liblp, AlignedFreeSpace) {
+TEST_F(BuilderTest, AlignedFreeSpace) {
// Only one sector free - at least one block is required.
- BlockDeviceInfo device_info(10240, 0, 0, 4096);
+ BlockDeviceInfo device_info("super", 10240, 0, 0, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 512, 1);
ASSERT_EQ(builder, nullptr);
}
-TEST(liblp, HasDefaultGroup) {
- BlockDeviceInfo device_info(1024 * 1024, 0, 0, 4096);
+TEST_F(BuilderTest, HasDefaultGroup) {
+ BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
ASSERT_NE(builder, nullptr);
EXPECT_FALSE(builder->AddGroup("default", 0));
}
-TEST(liblp, GroupSizeLimits) {
- BlockDeviceInfo device_info(1024 * 1024, 0, 0, 4096);
+TEST_F(BuilderTest, GroupSizeLimits) {
+ BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
ASSERT_NE(builder, nullptr);
@@ -530,8 +552,11 @@
constexpr unsigned long long operator"" _GiB(unsigned long long x) { // NOLINT
return x << 30;
}
+constexpr unsigned long long operator"" _MiB(unsigned long long x) { // NOLINT
+ return x << 20;
+}
-TEST(liblp, RemoveAndAddFirstPartition) {
+TEST_F(BuilderTest, RemoveAndAddFirstPartition) {
auto builder = MetadataBuilder::New(10_GiB, 65536, 2);
ASSERT_NE(nullptr, builder);
ASSERT_TRUE(builder->AddGroup("foo_a", 5_GiB));
@@ -554,8 +579,8 @@
ASSERT_TRUE(p && builder->ResizePartition(p, 1_GiB));
}
-TEST(liblp, ListGroups) {
- BlockDeviceInfo device_info(1024 * 1024, 0, 0, 4096);
+TEST_F(BuilderTest, ListGroups) {
+ BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
ASSERT_NE(builder, nullptr);
ASSERT_TRUE(builder->AddGroup("example", 0));
@@ -564,8 +589,8 @@
ASSERT_THAT(groups, ElementsAre("default", "example"));
}
-TEST(liblp, RemoveGroupAndPartitions) {
- BlockDeviceInfo device_info(1024 * 1024, 0, 0, 4096);
+TEST_F(BuilderTest, RemoveGroupAndPartitions) {
+ BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
ASSERT_NE(builder, nullptr);
ASSERT_TRUE(builder->AddGroup("example", 0));
@@ -580,3 +605,118 @@
builder->RemoveGroupAndPartitions("default");
ASSERT_NE(builder->FindPartition("system"), nullptr);
}
+
+TEST_F(BuilderTest, MultipleBlockDevices) {
+ std::vector<BlockDeviceInfo> partitions = {
+ BlockDeviceInfo("system_a", 256_MiB, 786432, 229376, 4096),
+ BlockDeviceInfo("vendor_a", 128_MiB, 786432, 753664, 4096),
+ BlockDeviceInfo("product_a", 64_MiB, 786432, 753664, 4096),
+ };
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(partitions, "system_a", 65536, 2);
+ ASSERT_NE(builder, nullptr);
+ EXPECT_EQ(builder->AllocatableSpace(), 467238912);
+
+ // Create a partition that spans 3 devices.
+ Partition* p = builder->AddPartition("system_a", 0);
+ ASSERT_NE(p, nullptr);
+ ASSERT_TRUE(builder->ResizePartition(p, 466976768));
+
+ unique_ptr<LpMetadata> metadata = builder->Export();
+ ASSERT_NE(metadata, nullptr);
+ ASSERT_EQ(metadata->block_devices.size(), 3);
+ EXPECT_EQ(GetBlockDevicePartitionName(metadata->block_devices[0]), "system_a");
+ EXPECT_EQ(metadata->block_devices[0].size, 256_MiB);
+ EXPECT_EQ(metadata->block_devices[0].alignment, 786432);
+ EXPECT_EQ(metadata->block_devices[0].alignment_offset, 229376);
+ EXPECT_EQ(GetBlockDevicePartitionName(metadata->block_devices[1]), "vendor_a");
+ EXPECT_EQ(metadata->block_devices[1].size, 128_MiB);
+ EXPECT_EQ(metadata->block_devices[1].alignment, 786432);
+ EXPECT_EQ(metadata->block_devices[1].alignment_offset, 753664);
+ EXPECT_EQ(GetBlockDevicePartitionName(metadata->block_devices[2]), "product_a");
+ EXPECT_EQ(metadata->block_devices[2].size, 64_MiB);
+ EXPECT_EQ(metadata->block_devices[2].alignment, 786432);
+ EXPECT_EQ(metadata->block_devices[2].alignment_offset, 753664);
+ ASSERT_EQ(metadata->extents.size(), 3);
+ EXPECT_EQ(metadata->extents[0].num_sectors, 522304);
+ EXPECT_EQ(metadata->extents[0].target_type, LP_TARGET_TYPE_LINEAR);
+ EXPECT_EQ(metadata->extents[0].target_data, 1984);
+ EXPECT_EQ(metadata->extents[0].target_source, 0);
+ EXPECT_EQ(metadata->extents[1].num_sectors, 260672);
+ EXPECT_EQ(metadata->extents[1].target_type, LP_TARGET_TYPE_LINEAR);
+ EXPECT_EQ(metadata->extents[1].target_data, 1472);
+ EXPECT_EQ(metadata->extents[1].target_source, 1);
+ EXPECT_EQ(metadata->extents[2].num_sectors, 129088);
+ EXPECT_EQ(metadata->extents[2].target_type, LP_TARGET_TYPE_LINEAR);
+ EXPECT_EQ(metadata->extents[2].target_data, 1472);
+ EXPECT_EQ(metadata->extents[2].target_source, 2);
+}
+
+TEST_F(BuilderTest, ImportPartitionsOk) {
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+ ASSERT_NE(builder, nullptr);
+
+ Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
+ Partition* vendor = builder->AddPartition("vendor", LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(system, nullptr);
+ ASSERT_NE(vendor, nullptr);
+ EXPECT_EQ(builder->ResizePartition(system, 65536), true);
+ EXPECT_EQ(builder->ResizePartition(vendor, 32768), true);
+ EXPECT_EQ(builder->ResizePartition(system, 98304), true);
+
+ unique_ptr<LpMetadata> exported = builder->Export();
+ ASSERT_NE(exported, nullptr);
+
+ builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+ ASSERT_NE(builder, nullptr);
+
+ ASSERT_TRUE(builder->ImportPartitions(*exported.get(), {"vendor"}));
+ EXPECT_NE(builder->FindPartition("vendor"), nullptr);
+ EXPECT_EQ(builder->FindPartition("system"), nullptr);
+
+ unique_ptr<LpMetadata> new_metadata = builder->Export();
+ ASSERT_NE(new_metadata, nullptr);
+
+ ASSERT_EQ(exported->partitions.size(), static_cast<size_t>(2));
+ ASSERT_EQ(GetPartitionName(exported->partitions[1]), "vendor");
+ ASSERT_EQ(new_metadata->partitions.size(), static_cast<size_t>(1));
+ ASSERT_EQ(GetPartitionName(new_metadata->partitions[0]), "vendor");
+
+ const LpMetadataExtent& extent_a =
+ exported->extents[exported->partitions[1].first_extent_index];
+ const LpMetadataExtent& extent_b =
+ new_metadata->extents[new_metadata->partitions[0].first_extent_index];
+ EXPECT_EQ(extent_a.num_sectors, extent_b.num_sectors);
+ EXPECT_EQ(extent_a.target_type, extent_b.target_type);
+ EXPECT_EQ(extent_a.target_data, extent_b.target_data);
+ EXPECT_EQ(extent_a.target_source, extent_b.target_source);
+}
+
+TEST_F(BuilderTest, ImportPartitionsFail) {
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+ ASSERT_NE(builder, nullptr);
+
+ Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
+ Partition* vendor = builder->AddPartition("vendor", LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(system, nullptr);
+ ASSERT_NE(vendor, nullptr);
+ EXPECT_EQ(builder->ResizePartition(system, 65536), true);
+ EXPECT_EQ(builder->ResizePartition(vendor, 32768), true);
+ EXPECT_EQ(builder->ResizePartition(system, 98304), true);
+
+ unique_ptr<LpMetadata> exported = builder->Export();
+ ASSERT_NE(exported, nullptr);
+
+ // Different device size.
+ builder = MetadataBuilder::New(1024 * 2048, 1024, 2);
+ ASSERT_NE(builder, nullptr);
+ EXPECT_FALSE(builder->ImportPartitions(*exported.get(), {"system"}));
+}
+
+TEST_F(BuilderTest, UnsuffixedPartitions) {
+ MetadataBuilder::OverrideABForTesting(true);
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+ ASSERT_NE(builder, nullptr);
+
+ ASSERT_EQ(builder->AddPartition("system", 0), nullptr);
+ ASSERT_NE(builder->AddPartition("system_a", 0), nullptr);
+}
diff --git a/fs_mgr/liblp/images.cpp b/fs_mgr/liblp/images.cpp
index 46bdfa4..5a498f9 100644
--- a/fs_mgr/liblp/images.cpp
+++ b/fs_mgr/liblp/images.cpp
@@ -27,6 +27,12 @@
namespace android {
namespace fs_mgr {
+using android::base::unique_fd;
+
+#if defined(_WIN32)
+static const int O_NOFOLLOW = 0;
+#endif
+
std::unique_ptr<LpMetadata> ReadFromImageFile(int fd) {
std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(LP_METADATA_GEOMETRY_SIZE);
if (SeekFile64(fd, 0, SEEK_SET) < 0) {
@@ -61,10 +67,10 @@
return ParseMetadata(geometry, metadata_buffer, metadata_buffer_size);
}
-std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file) {
- android::base::unique_fd fd(open(file, O_RDONLY));
+std::unique_ptr<LpMetadata> ReadFromImageFile(const std::string& image_file) {
+ unique_fd fd(open(image_file.c_str(), O_RDONLY | O_CLOEXEC));
if (fd < 0) {
- PERROR << __PRETTY_FUNCTION__ << " open failed: " << file;
+ PERROR << __PRETTY_FUNCTION__ << " open failed: " << image_file;
return nullptr;
}
return ReadFromImageFile(fd);
@@ -84,7 +90,7 @@
}
bool WriteToImageFile(const char* file, const LpMetadata& input) {
- android::base::unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC, 0644));
+ unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC, 0644));
if (fd < 0) {
PERROR << __PRETTY_FUNCTION__ << " open failed: " << file;
return false;
@@ -97,7 +103,6 @@
: metadata_(metadata),
geometry_(metadata.geometry),
block_size_(block_size),
- file_(nullptr, sparse_file_destroy),
images_(images) {
uint64_t total_size = GetTotalSuperPartitionSize(metadata);
if (block_size % LP_SECTOR_SIZE != 0) {
@@ -121,7 +126,7 @@
return;
}
- uint64_t num_blocks = total_size % block_size;
+ uint64_t num_blocks = total_size / block_size;
if (num_blocks >= UINT_MAX) {
// libsparse counts blocks in unsigned 32-bit integers, so we check to
// make sure we're not going to overflow.
@@ -129,20 +134,32 @@
return;
}
- file_.reset(sparse_file_new(block_size_, total_size));
- if (!file_) {
- LERROR << "Could not allocate sparse file of size " << total_size;
+ for (const auto& block_device : metadata.block_devices) {
+ SparsePtr file(sparse_file_new(block_size_, block_device.size), sparse_file_destroy);
+ if (!file) {
+ LERROR << "Could not allocate sparse file of size " << block_device.size;
+ return;
+ }
+ device_images_.emplace_back(std::move(file));
}
}
+bool SparseBuilder::IsValid() const {
+ return device_images_.size() == metadata_.block_devices.size();
+}
+
bool SparseBuilder::Export(const char* file) {
- android::base::unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC, 0644));
+ unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC, 0644));
if (fd < 0) {
PERROR << "open failed: " << file;
return false;
}
+ if (device_images_.size() > 1) {
+ LERROR << "Cannot export to a single image on retrofit builds.";
+ return false;
+ }
// No gzip compression; sparseify; no checksum.
- int ret = sparse_file_write(file_.get(), fd, false, true, false);
+ int ret = sparse_file_write(device_images_[0].get(), fd, false, true, false);
if (ret != 0) {
LERROR << "sparse_file_write failed (error code " << ret << ")";
return false;
@@ -150,13 +167,35 @@
return true;
}
-bool SparseBuilder::AddData(const std::string& blob, uint64_t sector) {
+bool SparseBuilder::ExportFiles(const std::string& output_dir) {
+ for (size_t i = 0; i < device_images_.size(); i++) {
+ std::string name = GetBlockDevicePartitionName(metadata_.block_devices[i]);
+ std::string file_name = "super_" + name + ".img";
+ std::string file_path = output_dir + "/" + file_name;
+
+ static const int kOpenFlags = O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC | O_NOFOLLOW;
+ unique_fd fd(open(file_path.c_str(), kOpenFlags, 0644));
+ if (fd < 0) {
+ PERROR << "open failed: " << file_path;
+ return false;
+ }
+ // No gzip compression; sparseify; no checksum.
+ int ret = sparse_file_write(device_images_[i].get(), fd, false, true, false);
+ if (ret != 0) {
+ LERROR << "sparse_file_write failed (error code " << ret << ")";
+ return false;
+ }
+ }
+ return true;
+}
+
+bool SparseBuilder::AddData(sparse_file* file, const std::string& blob, uint64_t sector) {
uint32_t block;
if (!SectorToBlock(sector, &block)) {
return false;
}
void* data = const_cast<char*>(blob.data());
- int ret = sparse_file_add_data(file_.get(), data, blob.size(), block);
+ int ret = sparse_file_add_data(file, data, blob.size(), block);
if (ret != 0) {
LERROR << "sparse_file_add_data failed (error code " << ret << ")";
return false;
@@ -179,8 +218,12 @@
return true;
}
+uint64_t SparseBuilder::BlockToSector(uint64_t block) const {
+ return (block * block_size_) / LP_SECTOR_SIZE;
+}
+
bool SparseBuilder::Build() {
- if (sparse_file_add_fill(file_.get(), 0, LP_PARTITION_RESERVED_BYTES, 0) < 0) {
+ if (sparse_file_add_fill(device_images_[0].get(), 0, LP_PARTITION_RESERVED_BYTES, 0) < 0) {
LERROR << "Could not add initial sparse block for reserved zeroes";
return false;
}
@@ -194,7 +237,13 @@
for (size_t i = 0; i < geometry_.metadata_slot_count * 2; i++) {
all_metadata_ += metadata_blob;
}
- if (!AddData(all_metadata_, 0)) {
+
+ uint64_t first_sector = LP_PARTITION_RESERVED_BYTES / LP_SECTOR_SIZE;
+ if (!AddData(device_images_[0].get(), all_metadata_, first_sector)) {
+ return false;
+ }
+
+ if (!CheckExtentOrdering()) {
return false;
}
@@ -228,13 +277,10 @@
bool SparseBuilder::AddPartitionImage(const LpMetadataPartition& partition,
const std::string& file) {
- if (partition.num_extents != 1) {
- LERROR << "Partition for new tables should not have more than one extent: "
- << GetPartitionName(partition);
- return false;
- }
+ // Track which extent we're processing.
+ uint32_t extent_index = partition.first_extent_index;
- const LpMetadataExtent& extent = metadata_.extents[partition.first_extent_index];
+ const LpMetadataExtent& extent = metadata_.extents[extent_index];
if (extent.target_type != LP_TARGET_TYPE_LINEAR) {
LERROR << "Partition should only have linear extents: " << GetPartitionName(partition);
return false;
@@ -252,9 +298,11 @@
LERROR << "Could not compute image size";
return false;
}
- if (file_length > extent.num_sectors * LP_SECTOR_SIZE) {
+ uint64_t partition_size = ComputePartitionSize(partition);
+ if (file_length > partition_size) {
LERROR << "Image for partition '" << GetPartitionName(partition)
- << "' is greater than its size";
+ << "' is greater than its size (" << file_length << ", expected " << partition_size
+ << ")";
return false;
}
if (SeekFile64(fd, 0, SEEK_SET)) {
@@ -262,14 +310,39 @@
return false;
}
+ // We track the current logical sector and the position the current extent
+ // ends at.
+ uint64_t output_sector = 0;
+ uint64_t extent_last_sector = extent.num_sectors;
+
+ // We also track the output device and the current output block within that
+ // device.
uint32_t output_block;
if (!SectorToBlock(extent.target_data, &output_block)) {
return false;
}
+ sparse_file* output_device = device_images_[extent.target_source].get();
+ // Proceed to read the file and build sparse images.
uint64_t pos = 0;
uint64_t remaining = file_length;
while (remaining) {
+ // Check if we need to advance to the next extent.
+ if (output_sector == extent_last_sector) {
+ extent_index++;
+ if (extent_index >= partition.first_extent_index + partition.num_extents) {
+ LERROR << "image is larger than extent table";
+ return false;
+ }
+
+ const LpMetadataExtent& extent = metadata_.extents[extent_index];
+ extent_last_sector += extent.num_sectors;
+ output_device = device_images_[extent.target_source].get();
+ if (!SectorToBlock(extent.target_data, &output_block)) {
+ return false;
+ }
+ }
+
uint32_t buffer[block_size_ / sizeof(uint32_t)];
size_t read_size = remaining >= sizeof(buffer) ? sizeof(buffer) : size_t(remaining);
if (!android::base::ReadFully(fd, buffer, sizeof(buffer))) {
@@ -277,13 +350,13 @@
return false;
}
if (read_size != sizeof(buffer) || !HasFillValue(buffer, read_size / sizeof(uint32_t))) {
- int rv = sparse_file_add_fd(file_.get(), fd, pos, read_size, output_block);
+ int rv = sparse_file_add_fd(output_device, fd, pos, read_size, output_block);
if (rv) {
LERROR << "sparse_file_add_fd failed with code: " << rv;
return false;
}
} else {
- int rv = sparse_file_add_fill(file_.get(), buffer[0], read_size, output_block);
+ int rv = sparse_file_add_fill(output_device, buffer[0], read_size, output_block);
if (rv) {
LERROR << "sparse_file_add_fill failed with code: " << rv;
return false;
@@ -291,46 +364,76 @@
}
pos += read_size;
remaining -= read_size;
+ output_sector += block_size_ / LP_SECTOR_SIZE;
output_block++;
}
return true;
}
+uint64_t SparseBuilder::ComputePartitionSize(const LpMetadataPartition& partition) const {
+ uint64_t sectors = 0;
+ for (size_t i = 0; i < partition.num_extents; i++) {
+ sectors += metadata_.extents[partition.first_extent_index + i].num_sectors;
+ }
+ return sectors * LP_SECTOR_SIZE;
+}
+
+// For simplicity, we don't allow serializing any configuration: extents must
+// be ordered, such that any extent at position I in the table occurs *before*
+// any extent after position I, for the same block device. We validate that
+// here.
+//
+// Without this, it would be more difficult to find the appropriate extent for
+// an output block. With this guarantee it is a linear walk.
+bool SparseBuilder::CheckExtentOrdering() {
+ std::vector<uint64_t> last_sectors(metadata_.block_devices.size());
+
+ for (const auto& extent : metadata_.extents) {
+ if (extent.target_type != LP_TARGET_TYPE_LINEAR) {
+ LERROR << "Extents must all be type linear.";
+ return false;
+ }
+ if (extent.target_data <= last_sectors[extent.target_source]) {
+ LERROR << "Extents must appear in increasing order.";
+ return false;
+ }
+ if ((extent.num_sectors * LP_SECTOR_SIZE) % block_size_ != 0) {
+ LERROR << "Extents must be aligned to the block size.";
+ return false;
+ }
+ last_sectors[extent.target_source] = extent.target_data;
+ }
+ return true;
+}
+
int SparseBuilder::OpenImageFile(const std::string& file) {
- android::base::unique_fd source_fd(open(file.c_str(), O_RDONLY));
+ android::base::unique_fd source_fd(open(file.c_str(), O_RDONLY | O_CLOEXEC));
if (source_fd < 0) {
PERROR << "open image file failed: " << file;
return -1;
}
- std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)> source(
- sparse_file_import(source_fd, true, true), sparse_file_destroy);
+ SparsePtr source(sparse_file_import(source_fd, true, true), sparse_file_destroy);
if (!source) {
int fd = source_fd.get();
temp_fds_.push_back(std::move(source_fd));
return fd;
}
- char temp_file[PATH_MAX];
- snprintf(temp_file, sizeof(temp_file), "%s/imageXXXXXX", P_tmpdir);
- android::base::unique_fd temp_fd(mkstemp(temp_file));
- if (temp_fd < 0) {
- PERROR << "mkstemp failed";
- return -1;
- }
- if (unlink(temp_file) < 0) {
- PERROR << "unlink failed";
+ TemporaryFile tf;
+ if (tf.fd < 0) {
+ PERROR << "make temporary file failed";
return -1;
}
// We temporarily unsparse the file, rather than try to merge its chunks.
- int rv = sparse_file_write(source.get(), temp_fd, false, false, false);
+ int rv = sparse_file_write(source.get(), tf.fd, false, false, false);
if (rv) {
LERROR << "sparse_file_write failed with code: " << rv;
return -1;
}
- temp_fds_.push_back(std::move(temp_fd));
+ temp_fds_.push_back(android::base::unique_fd(tf.release()));
return temp_fds_.back().get();
}
@@ -340,5 +443,11 @@
return builder.IsValid() && builder.Build() && builder.Export(file);
}
+bool WriteSplitSparseFiles(const std::string& output_dir, const LpMetadata& metadata,
+ uint32_t block_size, const std::map<std::string, std::string>& images) {
+ SparseBuilder builder(metadata, block_size, images);
+ return builder.IsValid() && builder.Build() && builder.ExportFiles(output_dir);
+}
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/liblp/images.h b/fs_mgr/liblp/images.h
index a9ef8ce..44217a0 100644
--- a/fs_mgr/liblp/images.h
+++ b/fs_mgr/liblp/images.h
@@ -42,20 +42,26 @@
bool Build();
bool Export(const char* file);
- bool IsValid() const { return file_ != nullptr; }
+ bool ExportFiles(const std::string& dir);
+ bool IsValid() const;
- sparse_file* file() const { return file_.get(); }
+ using SparsePtr = std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)>;
+ const std::vector<SparsePtr>& device_images() const { return device_images_; }
private:
- bool AddData(const std::string& blob, uint64_t sector);
+ bool AddData(sparse_file* file, const std::string& blob, uint64_t sector);
bool AddPartitionImage(const LpMetadataPartition& partition, const std::string& file);
int OpenImageFile(const std::string& file);
bool SectorToBlock(uint64_t sector, uint32_t* block);
+ uint64_t BlockToSector(uint64_t block) const;
+ bool CheckExtentOrdering();
+ uint64_t ComputePartitionSize(const LpMetadataPartition& partition) const;
const LpMetadata& metadata_;
const LpMetadataGeometry& geometry_;
uint32_t block_size_;
- std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)> file_;
+
+ std::vector<SparsePtr> device_images_;
std::string all_metadata_;
std::map<std::string, std::string> images_;
std::vector<android::base::unique_fd> temp_fds_;
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index a090889..4bb38d6 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -22,6 +22,8 @@
#include <map>
#include <memory>
+#include <optional>
+#include <set>
#include "liblp.h"
#include "partition_opener.h"
@@ -41,7 +43,7 @@
explicit Extent(uint64_t num_sectors) : num_sectors_(num_sectors) {}
virtual ~Extent() {}
- virtual void AddTo(LpMetadata* out) const = 0;
+ virtual bool AddTo(LpMetadata* out) const = 0;
virtual LinearExtent* AsLinearExtent() { return nullptr; }
uint64_t num_sectors() const { return num_sectors_; }
@@ -54,16 +56,18 @@
// This corresponds to a dm-linear target.
class LinearExtent final : public Extent {
public:
- LinearExtent(uint64_t num_sectors, uint64_t physical_sector)
- : Extent(num_sectors), physical_sector_(physical_sector) {}
+ LinearExtent(uint64_t num_sectors, uint32_t device_index, uint64_t physical_sector)
+ : Extent(num_sectors), device_index_(device_index), physical_sector_(physical_sector) {}
- void AddTo(LpMetadata* metadata) const override;
+ bool AddTo(LpMetadata* metadata) const override;
LinearExtent* AsLinearExtent() override { return this; }
uint64_t physical_sector() const { return physical_sector_; }
uint64_t end_sector() const { return physical_sector_ + num_sectors_; }
+ uint32_t device_index() const { return device_index_; }
private:
+ uint32_t device_index_;
uint64_t physical_sector_;
};
@@ -72,7 +76,7 @@
public:
explicit ZeroExtent(uint64_t num_sectors) : Extent(num_sectors) {}
- void AddTo(LpMetadata* out) const override;
+ bool AddTo(LpMetadata* out) const override;
};
class PartitionGroup final {
@@ -122,15 +126,17 @@
class MetadataBuilder {
public:
- // Construct an empty logical partition table builder. The block device size
- // and maximum metadata size must be specified, as this will determine which
- // areas of the physical partition can be flashed for metadata vs for logical
- // partitions.
+ // Construct an empty logical partition table builder given the specified
+ // map of partitions that are available for storing logical partitions.
+ //
+ // At least one partition in the list must be the "super" device, where
+ // metadata will be stored.
//
// If the parameters would yield invalid metadata, nullptr is returned. This
- // could happen if the block device size is too small to store the metadata
- // and backup copies.
- static std::unique_ptr<MetadataBuilder> New(const BlockDeviceInfo& device_info,
+ // could happen if the super device is too small to store all required
+ // metadata.
+ static std::unique_ptr<MetadataBuilder> New(const std::vector<BlockDeviceInfo>& block_devices,
+ const std::string& super_partition,
uint32_t metadata_max_size,
uint32_t metadata_slot_count);
@@ -145,19 +151,45 @@
static std::unique_ptr<MetadataBuilder> New(const std::string& super_partition,
uint32_t slot_number);
+ // This is when performing an A/B update. The source partition must be a
+ // super partition. On a normal device, the metadata for the source slot
+ // is imported and the target slot is ignored. On a retrofit device, the
+ // metadata may not have the target slot's devices listed yet, in which
+ // case, it is automatically upgraded to include all available block
+ // devices.
+ static std::unique_ptr<MetadataBuilder> NewForUpdate(const IPartitionOpener& opener,
+ const std::string& source_partition,
+ uint32_t source_slot_number,
+ uint32_t target_slot_number);
+
// Import an existing table for modification. If the table is not valid, for
// example it contains duplicate partition names, then nullptr is returned.
- // This method is for testing or changing off-line tables.
- static std::unique_ptr<MetadataBuilder> New(const LpMetadata& metadata);
+ //
+ // If an IPartitionOpener is specified, then block device informatiom will
+ // be updated.
+ static std::unique_ptr<MetadataBuilder> New(const LpMetadata& metadata,
+ const IPartitionOpener* opener = nullptr);
+
+ // Helper function for a single super partition, for tests.
+ static std::unique_ptr<MetadataBuilder> New(const BlockDeviceInfo& device_info,
+ uint32_t metadata_max_size,
+ uint32_t metadata_slot_count) {
+ return New({device_info}, device_info.partition_name, metadata_max_size,
+ metadata_slot_count);
+ }
// Wrapper around New() with a BlockDeviceInfo that only specifies a device
// size. This is a convenience method for tests.
static std::unique_ptr<MetadataBuilder> New(uint64_t blockdev_size, uint32_t metadata_max_size,
uint32_t metadata_slot_count) {
- BlockDeviceInfo device_info(blockdev_size, 0, 0, kDefaultBlockSize);
+ BlockDeviceInfo device_info(LP_METADATA_DEFAULT_PARTITION_NAME, blockdev_size, 0, 0,
+ kDefaultBlockSize);
return New(device_info, metadata_max_size, metadata_slot_count);
}
+ // Used by the test harness to override whether the device is "A/B".
+ static void OverrideABForTesting(bool ab_device);
+
// Define a new partition group. By default there is one group called
// "default", with an unrestricted size. A non-zero size will restrict the
// total space used by all partitions in the group.
@@ -209,8 +241,19 @@
// Remove all partitions belonging to a group, then remove the group.
void RemoveGroupAndPartitions(const std::string& group_name);
- bool GetBlockDeviceInfo(BlockDeviceInfo* info) const;
- bool UpdateBlockDeviceInfo(const BlockDeviceInfo& info);
+ // Set the LP_METADATA_AUTO_SLOT_SUFFIXING flag.
+ void SetAutoSlotSuffixing();
+
+ bool GetBlockDeviceInfo(const std::string& partition_name, BlockDeviceInfo* info) const;
+ bool UpdateBlockDeviceInfo(const std::string& partition_name, const BlockDeviceInfo& info);
+
+ // Attempt to preserve the named partitions from an older metadata. If this
+ // is not possible (for example, the block device list has changed) then
+ // false is returned.
+ bool ImportPartitions(const LpMetadata& metadata, const std::set<std::string>& partition_names);
+
+ // Return true if a block device is found, else false.
+ bool HasBlockDevice(const std::string& partition_name) const;
private:
MetadataBuilder();
@@ -218,19 +261,32 @@
MetadataBuilder(MetadataBuilder&&) = delete;
MetadataBuilder& operator=(const MetadataBuilder&) = delete;
MetadataBuilder& operator=(MetadataBuilder&&) = delete;
- bool Init(const BlockDeviceInfo& info, uint32_t metadata_max_size, uint32_t metadata_slot_count);
+ bool Init(const std::vector<BlockDeviceInfo>& block_devices, const std::string& super_partition,
+ uint32_t metadata_max_size, uint32_t metadata_slot_count);
bool Init(const LpMetadata& metadata);
bool GrowPartition(Partition* partition, uint64_t aligned_size);
void ShrinkPartition(Partition* partition, uint64_t aligned_size);
- uint64_t AlignSector(uint64_t sector) const;
+ uint64_t AlignSector(const LpMetadataBlockDevice& device, uint64_t sector) const;
uint64_t TotalSizeOfGroup(PartitionGroup* group) const;
+ bool UpdateBlockDeviceInfo(size_t index, const BlockDeviceInfo& info);
+ bool FindBlockDeviceByName(const std::string& partition_name, uint32_t* index) const;
+ bool ValidatePartitionSizeChange(Partition* partition, uint64_t old_size, uint64_t new_size);
+ void ImportExtents(Partition* dest, const LpMetadata& metadata,
+ const LpMetadataPartition& source);
+ bool ImportPartition(const LpMetadata& metadata, const LpMetadataPartition& source);
+ bool IsABDevice() const;
struct Interval {
+ uint32_t device_index;
uint64_t start;
uint64_t end;
- Interval(uint64_t start, uint64_t end) : start(start), end(end) {}
+ Interval(uint32_t device_index, uint64_t start, uint64_t end)
+ : device_index(device_index), start(start), end(end) {}
uint64_t length() const { return end - start; }
+
+ // Note: the device index is not included in sorting (intervals are
+ // sorted in per-device lists).
bool operator<(const Interval& other) const {
return (start == other.start) ? end < other.end : start < other.start;
}
@@ -239,14 +295,15 @@
void ExtentsToFreeList(const std::vector<Interval>& extents,
std::vector<Interval>* free_regions) const;
- const LpMetadataBlockDevice& super_device() const { return block_devices_[0]; }
- LpMetadataBlockDevice& super_device() { return block_devices_[0]; }
+ static bool sABOverrideValue;
+ static bool sABOverrideSet;
LpMetadataGeometry geometry_;
LpMetadataHeader header_;
std::vector<std::unique_ptr<Partition>> partitions_;
std::vector<std::unique_ptr<PartitionGroup>> groups_;
std::vector<LpMetadataBlockDevice> block_devices_;
+ bool auto_slot_suffixing_;
};
// Read BlockDeviceInfo for a given block device. This always returns false
diff --git a/fs_mgr/liblp/include/liblp/liblp.h b/fs_mgr/liblp/include/liblp/liblp.h
index 4669cea..6348f55 100644
--- a/fs_mgr/liblp/include/liblp/liblp.h
+++ b/fs_mgr/liblp/include/liblp/liblp.h
@@ -75,9 +75,17 @@
bool WriteToSparseFile(const char* file, const LpMetadata& metadata, uint32_t block_size,
const std::map<std::string, std::string>& images);
bool WriteToImageFile(const char* file, const LpMetadata& metadata);
-std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file);
+std::unique_ptr<LpMetadata> ReadFromImageFile(const std::string& image_file);
std::unique_ptr<LpMetadata> ReadFromImageBlob(const void* data, size_t bytes);
+// Similar to WriteToSparseFile, this will generate an image that can be
+// flashed to a device directly. However unlike WriteToSparseFile, it
+// is intended for retrofit devices, and will generate one sparse file per
+// block device (each named super_<name>.img) and placed in the specified
+// output folder.
+bool WriteSplitSparseFiles(const std::string& output_dir, const LpMetadata& metadata,
+ uint32_t block_size, const std::map<std::string, std::string>& images);
+
// Helper to extract safe C++ strings from partition info.
std::string GetPartitionName(const LpMetadataPartition& partition);
std::string GetPartitionGroupName(const LpMetadataPartitionGroup& group);
@@ -90,8 +98,13 @@
// Return the total size of all partitions comprising the super partition.
uint64_t GetTotalSuperPartitionSize(const LpMetadata& metadata);
-// Helper to return a slot number for a slot suffix.
+// Get the list of block device names required by the given metadata.
+std::vector<std::string> GetBlockDevicePartitionNames(const LpMetadata& metadata);
+
+// Slot suffix helpers.
uint32_t SlotNumberForSlotSuffix(const std::string& suffix);
+std::string SlotSuffixForSlotNumber(uint32_t slot_number);
+std::string GetPartitionSlotSuffix(const std::string& partition_name);
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/liblp/include/liblp/metadata_format.h b/fs_mgr/liblp/include/liblp/metadata_format.h
index 8a309be..9c5ec5c 100644
--- a/fs_mgr/liblp/include/liblp/metadata_format.h
+++ b/fs_mgr/liblp/include/liblp/metadata_format.h
@@ -38,7 +38,7 @@
#define LP_METADATA_HEADER_MAGIC 0x414C5030
/* Current metadata version. */
-#define LP_METADATA_MAJOR_VERSION 7
+#define LP_METADATA_MAJOR_VERSION 10
#define LP_METADATA_MINOR_VERSION 0
/* Attributes for the LpMetadataPartition::attributes field.
@@ -47,10 +47,19 @@
* device mapper, the block device will be created as read-only.
*/
#define LP_PARTITION_ATTR_NONE 0x0
-#define LP_PARTITION_ATTR_READONLY 0x1
+#define LP_PARTITION_ATTR_READONLY (1 << 0)
+
+/* This flag is only intended to be used with super_empty.img and super.img on
+ * retrofit devices. On these devices there are A and B super partitions, and
+ * we don't know ahead of time which slot the image will be applied to.
+ *
+ * If set, the partition name needs a slot suffix applied. The slot suffix is
+ * determined by the metadata slot number (0 = _a, 1 = _b).
+ */
+#define LP_PARTITION_ATTR_SLOT_SUFFIXED (1 << 1)
/* Mask that defines all valid attributes. */
-#define LP_PARTITION_ATTRIBUTE_MASK (LP_PARTITION_ATTR_READONLY)
+#define LP_PARTITION_ATTRIBUTE_MASK (LP_PARTITION_ATTR_READONLY | LP_PARTITION_ATTR_SLOT_SUFFIXED)
/* Default name of the physical partition that holds logical partition entries.
* The layout of this partition will look like:
@@ -240,6 +249,13 @@
* ZERO: This field must be 0.
*/
uint64_t target_data;
+
+ /* 20: Contents depends on target_type.
+ *
+ * LINEAR: Must be an index into the block devices table.
+ * ZERO: This field must be 0.
+ */
+ uint32_t target_source;
} __attribute__((packed)) LpMetadataExtent;
/* This struct defines an entry in the groups table. Each group has a maximum
@@ -251,12 +267,22 @@
/* 0: Name of this group. Any unused characters must be 0. */
char name[36];
- /* 36: Maximum size in bytes. If 0, the group has no maximum size. */
+ /* 36: Flags (see LP_GROUP_*). */
+ uint32_t flags;
+
+ /* 40: Maximum size in bytes. If 0, the group has no maximum size. */
uint64_t maximum_size;
} LpMetadataPartitionGroup;
-/* This struct defines an entry in the block_devices table. There must be
- * exactly one device, corresponding to the super partition.
+/* This flag is only intended to be used with super_empty.img and super.img on
+ * retrofit devices. If set, the group needs a slot suffix to be interpreted
+ * correctly. The suffix is automatically applied by ReadMetadata().
+ */
+#define LP_GROUP_SLOT_SUFFIXED (1 << 0)
+
+/* This struct defines an entry in the block_devices table. There must be at
+ * least one device, and the first device must represent the partition holding
+ * the super metadata.
*/
typedef struct LpMetadataBlockDevice {
/* 0: First usable sector for allocating logical partitions. this will be
@@ -294,8 +320,21 @@
/* 24: Partition name in the GPT. Any unused characters must be 0. */
char partition_name[36];
+
+ /* 60: Flags (see LP_BLOCK_DEVICE_* flags below). */
+ uint32_t flags;
} LpMetadataBlockDevice;
+/* This flag is only intended to be used with super_empty.img and super.img on
+ * retrofit devices. On these devices there are A and B super partitions, and
+ * we don't know ahead of time which slot the image will be applied to.
+ *
+ * If set, the block device needs a slot suffix applied before being used with
+ * IPartitionOpener. The slot suffix is determined by the metadata slot number
+ * (0 = _a, 1 = _b).
+ */
+#define LP_BLOCK_DEVICE_SLOT_SUFFIXED (1 << 0)
+
#ifdef __cplusplus
} /* extern "C" */
#endif
diff --git a/fs_mgr/liblp/include/liblp/partition_opener.h b/fs_mgr/liblp/include/liblp/partition_opener.h
index fe61b9c..e506bd5 100644
--- a/fs_mgr/liblp/include/liblp/partition_opener.h
+++ b/fs_mgr/liblp/include/liblp/partition_opener.h
@@ -27,12 +27,13 @@
struct BlockDeviceInfo {
BlockDeviceInfo() : size(0), alignment(0), alignment_offset(0), logical_block_size(0) {}
- BlockDeviceInfo(uint64_t size, uint32_t alignment, uint32_t alignment_offset,
- uint32_t logical_block_size)
+ BlockDeviceInfo(const std::string& partition_name, uint64_t size, uint32_t alignment,
+ uint32_t alignment_offset, uint32_t logical_block_size)
: size(size),
alignment(alignment),
alignment_offset(alignment_offset),
- logical_block_size(logical_block_size) {}
+ logical_block_size(logical_block_size),
+ partition_name(partition_name) {}
// Size of the block device, in bytes.
uint64_t size;
// Optimal target alignment, in bytes. Partition extents will be aligned to
@@ -44,6 +45,9 @@
uint32_t alignment_offset;
// Block size, for aligning extent sizes and partition sizes.
uint32_t logical_block_size;
+ // The physical partition name for this block device, as it would appear in
+ // the GPT or under /dev/block/by-name.
+ std::string partition_name;
};
// Test-friendly interface for interacting with partitions.
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
index 9c675fe..9f3314d 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -38,6 +38,7 @@
static const size_t kDiskSize = 131072;
static const size_t kMetadataSize = 512;
static const size_t kMetadataSlots = 2;
+static const BlockDeviceInfo kSuperInfo{"super", kDiskSize, 0, 0, 4096};
// Helper function for creating an in-memory file descriptor. This lets us
// simulate read/writing logical partition metadata as if we had a block device
@@ -79,6 +80,12 @@
return builder;
}
+class DefaultPartitionOpener final : public TestPartitionOpener {
+ public:
+ explicit DefaultPartitionOpener(int fd)
+ : TestPartitionOpener({{"super", fd}}, {{"super", kSuperInfo}}) {}
+};
+
static bool AddDefaultPartitions(MetadataBuilder* builder) {
Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_NONE);
if (!system) {
@@ -103,7 +110,7 @@
return {};
}
- TestPartitionOpener opener({{"super", fd}});
+ DefaultPartitionOpener opener(fd);
if (!FlashPartitionTable(opener, "super", *exported.get())) {
return {};
}
@@ -119,7 +126,7 @@
ASSERT_TRUE(GetDescriptorSize(fd, &size));
ASSERT_EQ(size, kDiskSize);
- TestPartitionOpener opener({{"super", fd}});
+ DefaultPartitionOpener opener(fd);
// Verify that we can't read unwritten metadata.
ASSERT_EQ(ReadMetadata(opener, "super", 1), nullptr);
@@ -128,7 +135,7 @@
// Flashing metadata should not work if the metadata was created for a larger
// disk than the destination disk.
TEST(liblp, ExportDiskTooSmall) {
- unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(kDiskSize + 1024, 512, 2);
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(kDiskSize + 4096, 512, 2);
ASSERT_NE(builder, nullptr);
unique_ptr<LpMetadata> exported = builder->Export();
ASSERT_NE(exported, nullptr);
@@ -138,7 +145,7 @@
unique_fd fd = CreateFakeDisk();
ASSERT_GE(fd, 0);
- TestPartitionOpener opener({{"super", fd}});
+ DefaultPartitionOpener opener(fd);
EXPECT_FALSE(FlashPartitionTable(opener, "super", *exported.get()));
}
@@ -152,7 +159,7 @@
unique_fd fd = CreateFakeDisk();
ASSERT_GE(fd, 0);
- TestPartitionOpener opener({{"super", fd}});
+ DefaultPartitionOpener opener(fd);
// Export and flash.
unique_ptr<LpMetadata> exported = builder->Export();
@@ -198,7 +205,7 @@
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
- TestPartitionOpener opener({{"super", fd}});
+ DefaultPartitionOpener opener(fd);
unique_ptr<LpMetadata> imported = ReadMetadata(opener, "super", 0);
ASSERT_NE(imported, nullptr);
@@ -243,7 +250,7 @@
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
- TestPartitionOpener opener({{"super", fd}});
+ DefaultPartitionOpener opener(fd);
// Make sure all slots are filled.
unique_ptr<LpMetadata> metadata = ReadMetadata(opener, "super", 0);
@@ -262,7 +269,7 @@
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
- TestPartitionOpener opener({{"super", fd}});
+ DefaultPartitionOpener opener(fd);
unique_ptr<LpMetadata> imported = ReadMetadata(opener, "super", 0);
ASSERT_NE(imported, nullptr);
@@ -291,7 +298,7 @@
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
- TestPartitionOpener opener({{"super", fd}});
+ DefaultPartitionOpener opener(fd);
LpMetadataGeometry geometry;
ASSERT_GE(lseek(fd, 0, SEEK_SET), 0);
@@ -310,7 +317,7 @@
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
- TestPartitionOpener opener({{"super", fd}});
+ DefaultPartitionOpener opener(fd);
char corruption[LP_METADATA_GEOMETRY_SIZE];
memset(corruption, 0xff, sizeof(corruption));
@@ -330,7 +337,7 @@
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
- TestPartitionOpener opener({{"super", fd}});
+ DefaultPartitionOpener opener(fd);
unique_ptr<LpMetadata> metadata = ReadMetadata(opener, "super", 0);
@@ -378,7 +385,7 @@
unique_fd fd = CreateFakeDisk();
ASSERT_GE(fd, 0);
- TestPartitionOpener opener({{"super", fd}});
+ DefaultPartitionOpener opener(fd);
// Check that we are able to write our table.
ASSERT_TRUE(FlashPartitionTable(opener, "super", *exported.get()));
@@ -487,7 +494,7 @@
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
- TestPartitionOpener opener({{"super", fd}});
+ DefaultPartitionOpener opener(fd);
BadWriter writer;
@@ -515,7 +522,7 @@
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
- TestPartitionOpener opener({{"super", fd}});
+ DefaultPartitionOpener opener(fd);
BadWriter writer;
@@ -544,7 +551,7 @@
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
- TestPartitionOpener opener({{"super", fd}});
+ DefaultPartitionOpener opener(fd);
BadWriter writer;
@@ -581,7 +588,7 @@
unique_fd fd = CreateFakeDisk();
ASSERT_GE(fd, 0);
- BlockDeviceInfo device_info(kDiskSize, 0, 0, 512);
+ BlockDeviceInfo device_info("super", kDiskSize, 0, 0, 512);
unique_ptr<MetadataBuilder> builder =
MetadataBuilder::New(device_info, kMetadataSize, kMetadataSlots);
ASSERT_NE(builder, nullptr);
@@ -593,12 +600,14 @@
// Build the sparse file.
SparseBuilder sparse(*exported.get(), 512, {});
ASSERT_TRUE(sparse.IsValid());
- sparse_file_verbose(sparse.file());
ASSERT_TRUE(sparse.Build());
+ const auto& images = sparse.device_images();
+ ASSERT_EQ(images.size(), static_cast<size_t>(1));
+
// Write it to the fake disk.
ASSERT_NE(lseek(fd.get(), 0, SEEK_SET), -1);
- int ret = sparse_file_write(sparse.file(), fd.get(), false, false, false);
+ int ret = sparse_file_write(images[0].get(), fd.get(), false, false, false);
ASSERT_EQ(ret, 0);
// Verify that we can read both sets of metadata.
@@ -608,3 +617,88 @@
ASSERT_NE(ReadPrimaryMetadata(fd.get(), geometry, 0), nullptr);
ASSERT_NE(ReadBackupMetadata(fd.get(), geometry, 0), nullptr);
}
+
+TEST(liblp, AutoSlotSuffixing) {
+ unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
+ ASSERT_NE(builder, nullptr);
+ ASSERT_TRUE(AddDefaultPartitions(builder.get()));
+ ASSERT_TRUE(builder->AddGroup("example", 0));
+ builder->SetAutoSlotSuffixing();
+
+ auto fd = CreateFakeDisk();
+ ASSERT_GE(fd, 0);
+
+ // Note: we bind the same fd to both names, since we want to make sure the
+ // exact same bits are getting read back in each test.
+ TestPartitionOpener opener({{"super_a", fd}, {"super_b", fd}},
+ {{"super_a", kSuperInfo}, {"super_b", kSuperInfo}});
+ auto exported = builder->Export();
+ ASSERT_NE(exported, nullptr);
+ ASSERT_TRUE(FlashPartitionTable(opener, "super_a", *exported.get()));
+
+ auto metadata = ReadMetadata(opener, "super_b", 1);
+ ASSERT_NE(metadata, nullptr);
+ ASSERT_EQ(metadata->partitions.size(), static_cast<size_t>(1));
+ EXPECT_EQ(GetPartitionName(metadata->partitions[0]), "system_b");
+ ASSERT_EQ(metadata->block_devices.size(), static_cast<size_t>(1));
+ EXPECT_EQ(GetBlockDevicePartitionName(metadata->block_devices[0]), "super_b");
+ ASSERT_EQ(metadata->groups.size(), static_cast<size_t>(2));
+ EXPECT_EQ(GetPartitionGroupName(metadata->groups[0]), "default");
+ EXPECT_EQ(GetPartitionGroupName(metadata->groups[1]), "example_b");
+ EXPECT_EQ(metadata->groups[0].flags, 0);
+ EXPECT_EQ(metadata->groups[1].flags, 0);
+
+ metadata = ReadMetadata(opener, "super_a", 0);
+ ASSERT_NE(metadata, nullptr);
+ ASSERT_EQ(metadata->partitions.size(), static_cast<size_t>(1));
+ EXPECT_EQ(GetPartitionName(metadata->partitions[0]), "system_a");
+ ASSERT_EQ(metadata->block_devices.size(), static_cast<size_t>(1));
+ EXPECT_EQ(GetBlockDevicePartitionName(metadata->block_devices[0]), "super_a");
+ ASSERT_EQ(metadata->groups.size(), static_cast<size_t>(2));
+ EXPECT_EQ(GetPartitionGroupName(metadata->groups[0]), "default");
+ EXPECT_EQ(GetPartitionGroupName(metadata->groups[1]), "example_a");
+ EXPECT_EQ(metadata->groups[0].flags, 0);
+ EXPECT_EQ(metadata->groups[1].flags, 0);
+}
+
+TEST(liblp, UpdateRetrofit) {
+ unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
+ ASSERT_NE(builder, nullptr);
+ ASSERT_TRUE(AddDefaultPartitions(builder.get()));
+ ASSERT_TRUE(builder->AddGroup("example", 0));
+ builder->SetAutoSlotSuffixing();
+
+ auto fd = CreateFakeDisk();
+ ASSERT_GE(fd, 0);
+
+ // Note: we bind the same fd to both names, since we want to make sure the
+ // exact same bits are getting read back in each test.
+ TestPartitionOpener opener({{"super_a", fd}, {"super_b", fd}},
+ {{"super_a", kSuperInfo}, {"super_b", kSuperInfo}});
+ auto exported = builder->Export();
+ ASSERT_NE(exported, nullptr);
+ ASSERT_TRUE(FlashPartitionTable(opener, "super_a", *exported.get()));
+
+ builder = MetadataBuilder::NewForUpdate(opener, "super_a", 0, 1);
+ ASSERT_NE(builder, nullptr);
+ auto updated = builder->Export();
+ ASSERT_NE(updated, nullptr);
+ ASSERT_EQ(updated->block_devices.size(), static_cast<size_t>(1));
+ EXPECT_EQ(GetBlockDevicePartitionName(updated->block_devices[0]), "super_b");
+ ASSERT_TRUE(updated->groups.empty());
+ ASSERT_TRUE(updated->partitions.empty());
+ ASSERT_TRUE(updated->extents.empty());
+}
+
+TEST(liblp, UpdateNonRetrofit) {
+ unique_fd fd = CreateFlashedDisk();
+ ASSERT_GE(fd, 0);
+
+ DefaultPartitionOpener opener(fd);
+ auto builder = MetadataBuilder::NewForUpdate(opener, "super", 0, 1);
+ ASSERT_NE(builder, nullptr);
+ auto updated = builder->Export();
+ ASSERT_NE(updated, nullptr);
+ ASSERT_EQ(updated->block_devices.size(), static_cast<size_t>(1));
+ EXPECT_EQ(GetBlockDevicePartitionName(updated->block_devices[0]), "super");
+}
diff --git a/fs_mgr/liblp/partition_opener.cpp b/fs_mgr/liblp/partition_opener.cpp
index 7381eed..898f241 100644
--- a/fs_mgr/liblp/partition_opener.cpp
+++ b/fs_mgr/liblp/partition_opener.cpp
@@ -19,11 +19,14 @@
#if defined(__linux__)
#include <linux/fs.h>
#endif
+#if !defined(_WIN32)
#include <sys/ioctl.h>
-#include <sys/stat.h>
+#endif
#include <sys/types.h>
#include <unistd.h>
+#include <android-base/file.h>
+
#include "utility.h"
namespace android {
@@ -51,23 +54,24 @@
return false;
}
if (ioctl(fd, BLKIOMIN, &device_info->alignment) < 0) {
- PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed";
+ PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed on " << block_device;
return false;
}
int alignment_offset;
if (ioctl(fd, BLKALIGNOFF, &alignment_offset) < 0) {
- PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed";
+ PERROR << __PRETTY_FUNCTION__ << "BLKALIGNOFF failed on " << block_device;
return false;
}
int logical_block_size;
if (ioctl(fd, BLKSSZGET, &logical_block_size) < 0) {
- PERROR << __PRETTY_FUNCTION__ << "BLKSSZGET failed";
+ PERROR << __PRETTY_FUNCTION__ << "BLKSSZGET failed on " << block_device;
return false;
}
device_info->alignment_offset = static_cast<uint32_t>(alignment_offset);
device_info->logical_block_size = static_cast<uint32_t>(logical_block_size);
+ device_info->partition_name = android::base::Basename(block_device);
return true;
#else
(void)block_device;
@@ -81,7 +85,7 @@
unique_fd PartitionOpener::Open(const std::string& partition_name, int flags) const {
std::string path = GetPartitionAbsolutePath(partition_name);
- return unique_fd{open(path.c_str(), flags)};
+ return unique_fd{open(path.c_str(), flags | O_CLOEXEC)};
}
bool PartitionOpener::GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const {
diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp
index 070573c..24c6b2c 100644
--- a/fs_mgr/liblp/reader.cpp
+++ b/fs_mgr/liblp/reader.cpp
@@ -274,6 +274,12 @@
memcpy(&extent, cursor, sizeof(extent));
cursor += header.extents.entry_size;
+ if (extent.target_type == LP_TARGET_TYPE_LINEAR &&
+ extent.target_source >= header.block_devices.num_entries) {
+ LERROR << "Logical partition extent has invalid block device.";
+ return nullptr;
+ }
+
metadata->extents.push_back(extent);
}
@@ -342,6 +348,49 @@
return ParseMetadata(geometry, fd);
}
+namespace {
+
+bool AdjustMetadataForSlot(LpMetadata* metadata, uint32_t slot_number) {
+ std::string slot_suffix = SlotSuffixForSlotNumber(slot_number);
+ for (auto& partition : metadata->partitions) {
+ if (!(partition.attributes & LP_PARTITION_ATTR_SLOT_SUFFIXED)) {
+ continue;
+ }
+ std::string partition_name = GetPartitionName(partition) + slot_suffix;
+ if (partition_name.size() > sizeof(partition.name)) {
+ LERROR << __PRETTY_FUNCTION__ << " partition name too long: " << partition_name;
+ return false;
+ }
+ strncpy(partition.name, partition_name.c_str(), sizeof(partition.name));
+ partition.attributes &= ~LP_PARTITION_ATTR_SLOT_SUFFIXED;
+ }
+ for (auto& block_device : metadata->block_devices) {
+ if (!(block_device.flags & LP_BLOCK_DEVICE_SLOT_SUFFIXED)) {
+ continue;
+ }
+ std::string partition_name = GetBlockDevicePartitionName(block_device) + slot_suffix;
+ if (!UpdateBlockDevicePartitionName(&block_device, partition_name)) {
+ LERROR << __PRETTY_FUNCTION__ << " partition name too long: " << partition_name;
+ return false;
+ }
+ block_device.flags &= ~LP_BLOCK_DEVICE_SLOT_SUFFIXED;
+ }
+ for (auto& group : metadata->groups) {
+ if (!(group.flags & LP_GROUP_SLOT_SUFFIXED)) {
+ continue;
+ }
+ std::string group_name = GetPartitionGroupName(group) + slot_suffix;
+ if (!UpdatePartitionGroupName(&group, group_name)) {
+ LERROR << __PRETTY_FUNCTION__ << " group name too long: " << group_name;
+ return false;
+ }
+ group.flags &= ~LP_GROUP_SLOT_SUFFIXED;
+ }
+ return true;
+}
+
+} // namespace
+
std::unique_ptr<LpMetadata> ReadMetadata(const IPartitionOpener& opener,
const std::string& super_partition, uint32_t slot_number) {
android::base::unique_fd fd = opener.Open(super_partition, O_RDONLY);
@@ -354,18 +403,30 @@
if (!ReadLogicalPartitionGeometry(fd, &geometry)) {
return nullptr;
}
-
if (slot_number >= geometry.metadata_slot_count) {
LERROR << __PRETTY_FUNCTION__ << " invalid metadata slot number";
return nullptr;
}
- // Read the primary copy, and if that fails, try the backup.
- std::unique_ptr<LpMetadata> metadata = ReadPrimaryMetadata(fd, geometry, slot_number);
- if (metadata) {
- return metadata;
+ std::vector<int64_t> offsets = {
+ GetPrimaryMetadataOffset(geometry, slot_number),
+ GetBackupMetadataOffset(geometry, slot_number),
+ };
+ std::unique_ptr<LpMetadata> metadata;
+
+ for (const auto& offset : offsets) {
+ if (SeekFile64(fd, offset, SEEK_SET) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << " lseek failed, offset " << offset;
+ continue;
+ }
+ if ((metadata = ParseMetadata(geometry, fd)) != nullptr) {
+ break;
+ }
}
- return ReadBackupMetadata(fd, geometry, slot_number);
+ if (!metadata || !AdjustMetadataForSlot(metadata.get(), slot_number)) {
+ return nullptr;
+ }
+ return metadata;
}
std::unique_ptr<LpMetadata> ReadMetadata(const std::string& super_partition, uint32_t slot_number) {
diff --git a/fs_mgr/liblp/reader.h b/fs_mgr/liblp/reader.h
index d5d5188..7a2490b 100644
--- a/fs_mgr/liblp/reader.h
+++ b/fs_mgr/liblp/reader.h
@@ -38,7 +38,9 @@
bool ReadPrimaryGeometry(int fd, LpMetadataGeometry* geometry);
bool ReadBackupGeometry(int fd, LpMetadataGeometry* geometry);
-// These functions assume a valid geometry and slot number.
+// These functions assume a valid geometry and slot number, and do not obey
+// auto-slot-suffixing. They are used for tests and for checking whether
+// the metadata is coherent across primary and backup copies.
std::unique_ptr<LpMetadata> ReadPrimaryMetadata(int fd, const LpMetadataGeometry& geometry,
uint32_t slot_number);
std::unique_ptr<LpMetadata> ReadBackupMetadata(int fd, const LpMetadataGeometry& geometry,
diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp
index 742ad82..9ccabe9 100644
--- a/fs_mgr/liblp/utility.cpp
+++ b/fs_mgr/liblp/utility.cpp
@@ -29,6 +29,7 @@
namespace fs_mgr {
bool GetDescriptorSize(int fd, uint64_t* size) {
+#if !defined(_WIN32)
struct stat s;
if (fstat(fd, &s) < 0) {
PERROR << __PRETTY_FUNCTION__ << "fstat failed";
@@ -39,6 +40,7 @@
*size = get_block_device_size(fd);
return *size != 0;
}
+#endif
int64_t result = SeekFile64(fd, 0, SEEK_END);
if (result == -1) {
@@ -97,15 +99,15 @@
}
uint32_t SlotNumberForSlotSuffix(const std::string& suffix) {
- if (suffix.empty()) {
+ if (suffix.empty() || suffix == "a" || suffix == "_a") {
return 0;
- }
- if (suffix.size() != 2 || suffix[0] != '_' || suffix[1] < 'a') {
+ } else if (suffix == "b" || suffix == "_b") {
+ return 1;
+ } else {
LERROR << __PRETTY_FUNCTION__ << "slot '" << suffix
<< "' does not have a recognized format.";
return 0;
}
- return suffix[1] - 'a';
}
uint64_t GetTotalSuperPartitionSize(const LpMetadata& metadata) {
@@ -116,5 +118,42 @@
return size;
}
+std::vector<std::string> GetBlockDevicePartitionNames(const LpMetadata& metadata) {
+ std::vector<std::string> list;
+ for (const auto& block_device : metadata.block_devices) {
+ list.emplace_back(GetBlockDevicePartitionName(block_device));
+ }
+ return list;
+}
+
+std::string GetPartitionSlotSuffix(const std::string& partition_name) {
+ if (partition_name.size() <= 2) {
+ return "";
+ }
+ std::string suffix = partition_name.substr(partition_name.size() - 2);
+ return (suffix == "_a" || suffix == "_b") ? suffix : "";
+}
+
+std::string SlotSuffixForSlotNumber(uint32_t slot_number) {
+ CHECK(slot_number == 0 || slot_number == 1);
+ return (slot_number == 0) ? "_a" : "_b";
+}
+
+bool UpdateBlockDevicePartitionName(LpMetadataBlockDevice* device, const std::string& name) {
+ if (name.size() > sizeof(device->partition_name)) {
+ return false;
+ }
+ strncpy(device->partition_name, name.c_str(), sizeof(device->partition_name));
+ return true;
+}
+
+bool UpdatePartitionGroupName(LpMetadataPartitionGroup* group, const std::string& name) {
+ if (name.size() > sizeof(group->name)) {
+ return false;
+ }
+ strncpy(group->name, name.c_str(), sizeof(group->name));
+ return true;
+}
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/liblp/utility.h b/fs_mgr/liblp/utility.h
index 65e643b..8b70919 100644
--- a/fs_mgr/liblp/utility.h
+++ b/fs_mgr/liblp/utility.h
@@ -84,6 +84,10 @@
return aligned;
}
+// Update names from C++ strings.
+bool UpdateBlockDevicePartitionName(LpMetadataBlockDevice* device, const std::string& name);
+bool UpdatePartitionGroupName(LpMetadataPartitionGroup* group, const std::string& name);
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/liblp/utility_test.cpp b/fs_mgr/liblp/utility_test.cpp
index bdf6dfd..15f7fff 100644
--- a/fs_mgr/liblp/utility_test.cpp
+++ b/fs_mgr/liblp/utility_test.cpp
@@ -24,10 +24,17 @@
TEST(liblp, SlotNumberForSlotSuffix) {
EXPECT_EQ(SlotNumberForSlotSuffix(""), 0);
+ EXPECT_EQ(SlotNumberForSlotSuffix("a"), 0);
EXPECT_EQ(SlotNumberForSlotSuffix("_a"), 0);
+ EXPECT_EQ(SlotNumberForSlotSuffix("b"), 1);
EXPECT_EQ(SlotNumberForSlotSuffix("_b"), 1);
- EXPECT_EQ(SlotNumberForSlotSuffix("_c"), 2);
- EXPECT_EQ(SlotNumberForSlotSuffix("_d"), 3);
+ EXPECT_EQ(SlotNumberForSlotSuffix("_c"), 0);
+ EXPECT_EQ(SlotNumberForSlotSuffix("_d"), 0);
+}
+
+TEST(liblp, SlotSuffixForSlotNumber) {
+ EXPECT_EQ(SlotSuffixForSlotNumber(0), "_a");
+ EXPECT_EQ(SlotSuffixForSlotNumber(1), "_b");
}
TEST(liblp, GetMetadataOffset) {
@@ -60,3 +67,11 @@
EXPECT_EQ(AlignTo(32, 32, 30), 62);
EXPECT_EQ(AlignTo(17, 32, 30), 30);
}
+
+TEST(liblp, GetPartitionSlotSuffix) {
+ EXPECT_EQ(GetPartitionSlotSuffix("system"), "");
+ EXPECT_EQ(GetPartitionSlotSuffix("_"), "");
+ EXPECT_EQ(GetPartitionSlotSuffix("_a"), "");
+ EXPECT_EQ(GetPartitionSlotSuffix("system_a"), "_a");
+ EXPECT_EQ(GetPartitionSlotSuffix("system_b"), "_b");
+}
diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp
index c740bd4..d8195ca 100644
--- a/fs_mgr/liblp/writer.cpp
+++ b/fs_mgr/liblp/writer.cpp
@@ -82,12 +82,8 @@
// Perform sanity checks so we don't accidentally overwrite valid metadata
// with potentially invalid metadata, or random partition data with metadata.
-static bool ValidateAndSerializeMetadata(int fd, const LpMetadata& metadata, std::string* blob) {
- uint64_t blockdevice_size;
- if (!GetDescriptorSize(fd, &blockdevice_size)) {
- return false;
- }
-
+static bool ValidateAndSerializeMetadata(const IPartitionOpener& opener, const LpMetadata& metadata,
+ const std::string& slot_suffix, std::string* blob) {
const LpMetadataHeader& header = metadata.header;
const LpMetadataGeometry& geometry = metadata.geometry;
@@ -104,7 +100,7 @@
// metadata.
uint64_t reserved_size = LP_METADATA_GEOMETRY_SIZE +
uint64_t(geometry.metadata_max_size) * geometry.metadata_slot_count;
- uint64_t total_reserved = reserved_size * 2;
+ uint64_t total_reserved = LP_PARTITION_RESERVED_BYTES + reserved_size * 2;
const LpMetadataBlockDevice* super_device = GetMetadataSuperBlockDevice(metadata);
if (!super_device) {
@@ -112,15 +108,36 @@
return false;
}
- if (total_reserved > blockdevice_size ||
- total_reserved > super_device->first_logical_sector * LP_SECTOR_SIZE) {
+ if (total_reserved > super_device->first_logical_sector * LP_SECTOR_SIZE) {
LERROR << "Not enough space to store all logical partition metadata slots.";
return false;
}
- if (blockdevice_size != super_device->size) {
- LERROR << "Block device size " << blockdevice_size
- << " does not match metadata requested size " << super_device->size;
- return false;
+ for (const auto& block_device : metadata.block_devices) {
+ std::string partition_name = GetBlockDevicePartitionName(block_device);
+ if (block_device.flags & LP_BLOCK_DEVICE_SLOT_SUFFIXED) {
+ if (slot_suffix.empty()) {
+ LERROR << "Block device " << partition_name << " requires a slot suffix,"
+ << " which could not be derived from the super partition name.";
+ return false;
+ }
+ partition_name += slot_suffix;
+ }
+
+ if ((block_device.first_logical_sector + 1) * LP_SECTOR_SIZE > block_device.size) {
+ LERROR << "Block device " << partition_name << " has invalid first sector "
+ << block_device.first_logical_sector << " for size " << block_device.size;
+ return false;
+ }
+ BlockDeviceInfo info;
+ if (!opener.GetInfo(partition_name, &info)) {
+ PERROR << partition_name << ": ioctl";
+ return false;
+ }
+ if (info.size != block_device.size) {
+ LERROR << "Block device " << partition_name << " size mismatch (expected"
+ << block_device.size << ", got " << info.size << ")";
+ return false;
+ }
}
// Make sure all partition entries reference valid extents.
@@ -218,6 +235,10 @@
return android::base::WriteFully(fd, blob.data(), blob.size());
}
+#if defined(_WIN32)
+static const int O_SYNC = 0;
+#endif
+
bool FlashPartitionTable(const IPartitionOpener& opener, const std::string& super_partition,
const LpMetadata& metadata) {
android::base::unique_fd fd = opener.Open(super_partition, O_RDWR | O_SYNC);
@@ -226,11 +247,16 @@
return false;
}
+ // This is only used in update_engine and fastbootd, where the super
+ // partition should be specified as a name (or by-name link), and
+ // therefore, we should be able to extract a slot suffix.
+ std::string slot_suffix = GetPartitionSlotSuffix(super_partition);
+
// Before writing geometry and/or logical partition tables, perform some
// basic checks that the geometry and tables are coherent, and will fit
// on the given block device.
std::string metadata_blob;
- if (!ValidateAndSerializeMetadata(fd, metadata, &metadata_blob)) {
+ if (!ValidateAndSerializeMetadata(opener, metadata, slot_suffix, &metadata_blob)) {
return false;
}
@@ -291,11 +317,13 @@
return false;
}
+ std::string slot_suffix = SlotSuffixForSlotNumber(slot_number);
+
// Before writing geometry and/or logical partition tables, perform some
// basic checks that the geometry and tables are coherent, and will fit
// on the given block device.
std::string blob;
- if (!ValidateAndSerializeMetadata(fd, metadata, &blob)) {
+ if (!ValidateAndSerializeMetadata(opener, metadata, slot_suffix, &blob)) {
return false;
}
@@ -327,7 +355,7 @@
// synchronize the backup copy. This guarantees that a partial write
// still leaves one copy intact.
std::string old_blob;
- if (!ValidateAndSerializeMetadata(fd, *primary.get(), &old_blob)) {
+ if (!ValidateAndSerializeMetadata(opener, *primary.get(), slot_suffix, &old_blob)) {
LERROR << "Error serializing primary metadata to repair corrupted backup";
return false;
}
@@ -339,7 +367,7 @@
// The backup copy is coherent, and the primary is not. Sync it for
// safety.
std::string old_blob;
- if (!ValidateAndSerializeMetadata(fd, *backup.get(), &old_blob)) {
+ if (!ValidateAndSerializeMetadata(opener, *backup.get(), slot_suffix, &old_blob)) {
LERROR << "Error serializing primary metadata to repair corrupted backup";
return false;
}
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index 5ef4794..7a00194 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -1,14 +1,21 @@
#! /bin/bash
-#
-# adb remount tests (overlayfs focus)
-#
-# Conditions:
-# - Must be a userdebug build.
-# - Must be in adb mode.
-# - Kernel must have overlayfs enabled and patched to support override_creds.
-# - Must have either squashfs, ext4-dedupe or right-sized partitions.
-# - Minimum expectation system and vender are overlayfs covered partitions.
-#
+
+USAGE="USAGE: `basename ${0}` [-s <SerialNumber>]
+
+adb remount tests (overlayfs focus)
+
+Conditions:
+ - Must be a userdebug build.
+ - Must be in adb mode.
+ - Kernel must have overlayfs enabled and patched to support override_creds.
+ - Must have either squashfs, ext4-dedupe or right-sized partitions.
+ - Minimum expectation system and vender are overlayfs covered partitions.
+"
+
+if [ X"${1}" = X"--help" -o X"${1}" = X"-h" -o X"${1}" = X"-?" ]; then
+ echo "${USAGE}" >&2
+ exit 0
+fi
# Helper Variables
@@ -45,11 +52,27 @@
adb shell "${@}"
}
+[ "USAGE: adb_date >/dev/stdout
+
+Returns: report device epoch time (suitable for logcat -t)" ]
+adb_date() {
+ adb_sh date +%s.%N </dev/null
+}
+
+[ "USAGE: adb_logcat [arguments] >/dev/stdout
+
+Returns: the logcat output" ]
+adb_logcat() {
+ adb logcat "${@}" </dev/null |
+ grep -v 'logd : logdr: UID=' |
+ sed -e '${/------- beginning of kernel/d}' -e 's/^[0-1][0-9]-[0-3][0-9] //'
+}
+
[ "USAGE: get_property <prop>
Returns the property value" ]
get_property() {
- adb_sh getprop ${1} 2>&1 </dev/null
+ adb_sh getprop ${1} </dev/null
}
[ "USAGE: isDebuggable
@@ -86,24 +109,37 @@
adb reboot remount-test
}
-[ "USAGE: adb_wait
+[ "USAGE: adb_wait [timeout]
-Returns: waits until the device has returned" ]
+Returns: waits until the device has returned or the optional timeout" ]
adb_wait() {
- adb wait-for-device
+ if [ -n "${1}" ]; then
+ timeout --preserve-status --signal=KILL ${1} adb wait-for-device
+ else
+ adb wait-for-device
+ fi
}
[ "USAGE: adb_root
Returns: true if device in root state" ]
adb_root() {
- adb root >/dev/null </dev/null 2>&1 &&
+ adb root >/dev/null </dev/null 2>/dev/null &&
sleep 1 &&
adb_wait &&
sleep 1
}
+[ "USAGE: die [-t <epoch>] [message] >/dev/stderr
+
+If -t <epoch> argument is supplied, dump logcat.
+
+Returns: exit failure, report status" ]
die() {
+ if [ X"-t" = X"${1}" -a -n "${2}" ]; then
+ adb_logcat -b all -v nsec -t ${2} >&2
+ shift 2
+ fi
echo "${RED}[ FAILED ]${NORMAL} ${@}" >&2
exit 1
}
@@ -165,11 +201,15 @@
die "${@}"
}
-[ "USAGE: skip_administrative_mounts
+[ "USAGE: skip_administrative_mounts < /proc/mounts
-Filters out all administrative (eg: sysfs) mounts" ]
+Filters out all administrative (eg: sysfs) mounts uninteresting to the test" ]
skip_administrative_mounts() {
- grep -v -e "^\(overlay\|tmpfs\|none\|sysfs\|proc\|selinuxfs\|debugfs\|bpf\|cg2_bpf\|pstore\|tracefs\|adb\|mtp\|ptp\|devpts\|/data/media\) " -e " /\(cache\|mnt/scratch\|mnt/vendor/persist\|metadata\|data\) "
+ grep -v \
+ -e "^\(overlay\|tmpfs\|none\|sysfs\|proc\|selinuxfs\|debugfs\) " \
+ -e "^\(bpf\|cg2_bpf\|pstore\|tracefs\|adb\|mtp\|ptp\|devpts\) " \
+ -e "^\(/data/media\|/dev/block/loop[0-9]*\) " \
+ -e " /\(cache\|mnt/scratch\|mnt/vendor/persist\|metadata\|data\) "
}
if [ X"-s" = X"${1}" -a -n "${2}" ]; then
@@ -178,48 +218,101 @@
fi
inFastboot && die "device in fastboot mode"
+if ! inAdb; then
+ echo "${ORANGE}[ WARNING ]${NORMAL} device not in adb mode ... waiting 2 minutes"
+ adb_wait 2m
+fi
inAdb || die "device not in adb mode"
isDebuggable || die "device not a debug build"
# Do something
adb_wait || die "wait for device failed"
-adb_sh ls -d /sys/module/overlay </dev/null || die "overlay module not present"
-adb_su ls /sys/module/overlay/parameters/override_creds </dev/null ||
+adb_sh ls -d /sys/module/overlay </dev/null >/dev/null &&
+ echo "${GREEN}[ OK ]${NORMAL} overlay module present" >&2 ||
+ die "overlay module not present"
+adb_su ls /sys/module/overlay/parameters/override_creds </dev/null >/dev/null &&
+ echo "${GREEN}[ OK ]${NORMAL} overlay module supports override_creds" >&2 ||
die "overlay module can not be used on ANDROID"
adb_root &&
- adb_wait &&
- D=`adb disable-verity 2>&1` ||
- die "setup for overlay"
+ adb_wait ||
+ die "initial setup"
+reboot=false
+OVERLAYFS_BACKING="cache mnt/scratch"
+for d in ${OVERLAYFS_BACKING}; do
+ if adb_sh ls -d /${d}/overlay </dev/null >/dev/null 2>/dev/null; then
+ echo "${ORANGE}[ WARNING ]${NORMAL} /${d}/overlay is setup, wiping" >&2
+ adb_sh rm -rf /${d}/overlay </dev/null ||
+ die "/${d}/overlay wipe"
+ reboot=true
+ fi
+done
+if ${reboot}; then
+ echo "${ORANGE}[ WARNING ]${NORMAL} rebooting before test" >&2
+ adb_reboot &&
+ adb_wait 2m &&
+ adb_root &&
+ adb_wait ||
+ die "reboot after wipe"
+fi
+D=`adb_sh df -k </dev/null` &&
+ H=`echo "${D}" | head -1` &&
+ D=`echo "${D}" | grep "^overlay "` &&
+ echo "${H}" &&
+ echo "${D}" &&
+ echo "${ORANGE}[ WARNING ]${NORMAL} overlays present before setup" >&2 ||
+ echo "${GREEN}[ OK ]${NORMAL} no overlay present before setup" >&2
+adb_sh df -k `adb_sh cat /proc/mounts |
+ skip_administrative_mounts |
+ cut -s -d' ' -f1`
+
+T=`adb_date`
+D=`adb disable-verity 2>&1`
+err=${?}
echo "${D}"
+if [ ${err} != 0 -o X"${D}" != X"${D##*setup failed}" ]; then
+ die -t ${T} "setup for overlay"
+fi
if [ X"${D}" != X"${D##*using overlayfs}" ]; then
echo "${GREEN}[ OK ]${NORMAL} using overlayfs" >&2
fi
-if adb_sh ls -d /data/overlay </dev/null >/dev/null 2>&1; then
- echo "/data/overlay setup, clearing out" >&2
- adb_sh rm -rf /data/overlay </dev/null ||
- die "/data/overlay removal"
-fi
-adb_sh ls -d /cache/overlay </dev/null >/dev/null 2>&1 ||
- adb_sh ls -d /mnt/scratch/overlay </dev/null >/dev/null 2>&1 ||
- die "overlay directory setup"
adb_reboot &&
adb_wait &&
- adb_sh df -k </dev/null | head -1 &&
- adb_sh df -k </dev/null | grep "^overlay " &&
- adb_sh df -k </dev/null | grep "^overlay .* /system\$" >/dev/null ||
+ D=`adb_sh df -k </dev/null` &&
+ H=`echo "${D}" | head -1` &&
+ D=`echo "${D}" | grep "^overlay "` &&
+ echo "${H}" &&
+ echo "${D}" ||
+ die "overlay takeover failed"
+echo "${D}" | grep "^overlay .* /system\$" >/dev/null ||
echo "${ORANGE}[ WARNING ]${NORMAL} overlay takeover before remount not complete" >&2
+T=`adb_date`
adb_root &&
adb_wait &&
adb remount &&
- adb_sh df -k </dev/null | head -1 &&
- adb_sh df -k </dev/null | grep "^overlay " &&
- adb_sh df -k </dev/null | grep "^overlay .* /system\$" >/dev/null ||
+ D=`adb_sh df -k </dev/null` ||
+ die -t ${T} "can not collect filesystem data"
+if echo "${D}" | grep " /mnt/scratch" >/dev/null; then
+ echo "${ORANGE}[ INFO ]${NORMAL} using scratch dynamic partition for overrides" >&2
+ H=`adb_sh cat /proc/mounts | sed -n 's@\([^ ]*\) /mnt/scratch \([^ ]*\) .*@\2 on \1@p'`
+ [ -n "${H}" ] &&
+ echo "${ORANGE}[ INFO ]${NORMAL} scratch filesystem ${H}"
+fi
+for d in ${OVERLAYFS_BACKING}; do
+ if adb_sh ls -d /${d}/overlay/system/upper </dev/null >/dev/null 2>/dev/null; then
+ echo "${ORANGE}[ INFO ]${NORMAL} /${d}/overlay is setup" >&2
+ fi
+done
+
+H=`echo "${D}" | head -1` &&
+ D=`echo "${D}" | grep "^overlay "` &&
+ echo "${H}" &&
+ echo "${D}" &&
+ echo "${D}" | grep "^overlay .* /system\$" >/dev/null ||
die "overlay takeover after remount"
!(adb_sh grep "^overlay " /proc/mounts </dev/null | grep " overlay ro,") &&
- !(adb_sh grep " rw," /proc/mounts </dev/null |
- skip_administrative_mounts) ||
- die "remount overlayfs missed a spot"
+ !(adb_sh grep " rw," /proc/mounts </dev/null | skip_administrative_mounts) ||
+ die "remount overlayfs missed a spot (ro)"
adb_su "sed -n '1,/overlay \\/system/p' /proc/mounts" </dev/null |
skip_administrative_mounts |
@@ -256,14 +349,18 @@
fastboot flash vendor &&
fastboot reboot ||
die "fastbootd flash vendor"
-adb_wait &&
- adb_root &&
+adb_wait 2m ||
+ die "did not reboot after flash"
+adb_root &&
adb_wait &&
- adb_sh df -k </dev/null | head -1 &&
- adb_sh df -k </dev/null | grep "^overlay " &&
- adb_sh df -k </dev/null | grep "^overlay .* /system\$" >/dev/null ||
+ D=`adb_sh df -k </dev/null` &&
+ H=`echo "${D}" | head -1` &&
+ D=`echo "${D}" | grep "^overlay "` &&
+ echo "${H}" &&
+ echo "${D}" &&
+ echo "${D}" | grep "^overlay .* /system\$" >/dev/null ||
die "overlay system takeover after flash vendor"
-adb_sh df -k </dev/null | grep "^overlay .* /vendor\$" >/dev/null &&
+echo "${D}" | grep "^overlay .* /vendor\$" >/dev/null &&
die "overlay minus vendor takeover after flash vendor"
B="`adb_cat /system/hello`" ||
die "re-read system hello after flash vendor"
@@ -275,10 +372,11 @@
die "re-read vendor hello after flash vendor"
check_eq "cat: /vendor/hello: No such file or directory" "${B}" vendor after flash vendor
+T=`adb_date`
adb remount &&
( adb_sh rm /vendor/hello </dev/null 2>/dev/null || true ) &&
adb_sh rm /system/hello </dev/null ||
- die "cleanup hello"
+ die -t ${T} "cleanup hello"
B="`adb_cat /system/hello`" &&
die "re-read system hello after rm"
check_eq "cat: /system/hello: No such file or directory" "${B}" after flash rm
diff --git a/gatekeeperd/Android.bp b/gatekeeperd/Android.bp
new file mode 100644
index 0000000..8e9c7ea
--- /dev/null
+++ b/gatekeeperd/Android.bp
@@ -0,0 +1,50 @@
+//
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_binary {
+ name: "gatekeeperd",
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-Wunused",
+ ],
+ srcs: [
+ "SoftGateKeeperDevice.cpp",
+ "IGateKeeperService.cpp",
+ "gatekeeperd.cpp",
+ ],
+
+ shared_libs: [
+ "libbinder",
+ "libgatekeeper",
+ "liblog",
+ "libhardware",
+ "libbase",
+ "libutils",
+ "libcrypto",
+ "libkeystore_aidl",
+ "libkeystore_binder",
+ "libhidlbase",
+ "libhidltransport",
+ "libhwbinder",
+ "android.hardware.gatekeeper@1.0",
+ ],
+
+ static_libs: ["libscrypt_static"],
+ include_dirs: ["external/scrypt/lib/crypto"],
+ init_rc: ["gatekeeperd.rc"],
+}
diff --git a/gatekeeperd/Android.mk b/gatekeeperd/Android.mk
deleted file mode 100644
index 6d5d1ea..0000000
--- a/gatekeeperd/Android.mk
+++ /dev/null
@@ -1,47 +0,0 @@
-#
-# Copyright (C) 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_CFLAGS := -Wall -Wextra -Werror -Wunused
-LOCAL_SRC_FILES := \
- SoftGateKeeperDevice.cpp \
- IGateKeeperService.cpp \
- gatekeeperd.cpp
-
-LOCAL_MODULE := gatekeeperd
-LOCAL_SHARED_LIBRARIES := \
- libbinder \
- libgatekeeper \
- liblog \
- libhardware \
- libbase \
- libutils \
- libcrypto \
- libkeystore_aidl \
- libkeystore_binder \
- libhidlbase \
- libhidltransport \
- libhwbinder \
- android.hardware.gatekeeper@1.0 \
-
-LOCAL_STATIC_LIBRARIES := libscrypt_static
-LOCAL_C_INCLUDES := external/scrypt/lib/crypto
-LOCAL_INIT_RC := gatekeeperd.rc
-include $(BUILD_EXECUTABLE)
-
-include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index f2818f3..446b66e 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -25,7 +25,7 @@
#include <unistd.h>
#include <memory>
-#include <android/security/IKeystoreService.h>
+#include <android/security/keystore/IKeystoreService.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/PermissionCache.h>
@@ -318,8 +318,8 @@
// TODO: cache service?
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder = sm->getService(String16("android.security.keystore"));
- sp<security::IKeystoreService> service =
- interface_cast<security::IKeystoreService>(binder);
+ sp<security::keystore::IKeystoreService> service =
+ interface_cast<security::keystore::IKeystoreService>(binder);
if (service != NULL) {
std::vector<uint8_t> auth_token_vector(*auth_token,
(*auth_token) + *auth_token_length);
diff --git a/gatekeeperd/tests/Android.bp b/gatekeeperd/tests/Android.bp
new file mode 100644
index 0000000..d4cf93b
--- /dev/null
+++ b/gatekeeperd/tests/Android.bp
@@ -0,0 +1,34 @@
+//
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_test {
+ name: "gatekeeperd-unit-tests",
+
+ cflags: [
+ "-g",
+ "-Wall",
+ "-Werror",
+ "-Wno-missing-field-initializers",
+ ],
+ shared_libs: [
+ "libgatekeeper",
+ "libcrypto",
+ "libbase",
+ ],
+ static_libs: ["libscrypt_static"],
+ include_dirs: ["external/scrypt/lib/crypto"],
+ srcs: ["gatekeeper_test.cpp"],
+}
diff --git a/gatekeeperd/tests/Android.mk b/gatekeeperd/tests/Android.mk
deleted file mode 100644
index c38c64b..0000000
--- a/gatekeeperd/tests/Android.mk
+++ /dev/null
@@ -1,29 +0,0 @@
-#
-# Copyright (C) 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := gatekeeperd-unit-tests
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-LOCAL_CFLAGS += -g -Wall -Werror -Wno-missing-field-initializers
-LOCAL_SHARED_LIBRARIES := libgatekeeper libcrypto libbase
-LOCAL_STATIC_LIBRARIES := libscrypt_static
-LOCAL_C_INCLUDES := external/scrypt/lib/crypto
-LOCAL_SRC_FILES := \
- gatekeeper_test.cpp
-include $(BUILD_NATIVE_TEST)
-
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index 2a5667c..a1519da 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -240,10 +240,9 @@
if (readFromFile(mHealthdConfig->batteryTechnologyPath, &buf) > 0)
props.batteryTechnology = String8(buf.c_str());
- unsigned int i;
double MaxPower = 0;
- for (i = 0; i < mChargerNames.size(); i++) {
+ for (size_t i = 0; i < mChargerNames.size(); i++) {
String8 path;
path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH,
mChargerNames[i].string());
diff --git a/init/Android.bp b/init/Android.bp
index c793971..ff3b61f 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -74,6 +74,7 @@
"libdl",
"libext4_utils",
"libfs_mgr",
+ "libfscrypt",
"libhidl-gen-utils",
"libkeyutils",
"liblog",
diff --git a/init/Android.mk b/init/Android.mk
index ef08329..ee030c7 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -73,6 +73,7 @@
libsquashfs_utils \
liblogwrap \
libext4_utils \
+ libfscrypt \
libseccomp_policy \
libcrypto_utils \
libsparse \
@@ -89,6 +90,9 @@
libcap \
LOCAL_SANITIZE := signed-integer-overflow
+# First stage init is weird: it may start without stdout/stderr, and no /proc.
+LOCAL_NOSANITIZE := hwaddress
+LOCAL_XOM := false
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
diff --git a/init/README.md b/init/README.md
index 6c51b37..5c07ac1 100644
--- a/init/README.md
+++ b/init/README.md
@@ -235,9 +235,16 @@
to "123,124,125". Since keycodes are handled very early in init,
only PRODUCT_DEFAULT_PROPERTY_OVERRIDES properties can be used.
-`memcg.limit_in_bytes <value>`
-> Sets the child's memory.limit_in_bytes to the specified value (only if memcg is mounted),
- which must be equal or greater than 0.
+`memcg.limit_in_bytes <value>` and `memcg.limit_percent <value>`
+> Sets the child's memory.limit_in_bytes to the minimum of `limit_in_bytes`
+ bytes and `limit_percent` which is interpreted as a percentage of the size
+ of the device's physical memory (only if memcg is mounted).
+ Values must be equal or greater than 0.
+
+`memcg.limit_property <value>`
+> Sets the child's memory.limit_in_bytes to the value of the specified property
+ (only if memcg is mounted). This property will override the values specified
+ via `memcg.limit_in_bytes` and `memcg.limit_percent`.
`memcg.soft_limit_in_bytes <value>`
> Sets the child's memory.soft_limit_in_bytes to the specified value (only if memcg is mounted),
@@ -268,6 +275,10 @@
since it has some peculiarities for backwards compatibility reasons. The 'imports' section of
this file has more details on the order.
+`parse_apex_configs`
+ Parses config file(s) from the mounted APEXes. Intented to be used only once
+ when apexd notifies the mount event by setting apexd.status to ready.
+
`priority <priority>`
> Scheduling priority of the service process. This value has to be in range
-20 to 19. Default priority is 0. Priority is set via setpriority().
@@ -321,6 +332,13 @@
This is particularly useful for creating a periodic service combined with the restart_period
option described above.
+`updatable`
+> Mark that the service can be overridden (via the 'override' option) later in
+ the boot sequence by APEXes. When a service with updatable option is started
+ before APEXes are all activated, the execution is delayed until the activation
+ is finished. A service that is not marked as updatable cannot be overridden by
+ APEXes.
+
`user <username>`
> Change to 'username' before exec'ing this service.
Currently defaults to root. (??? probably should default to nobody)
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 7da2526..6eb65b2 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -20,6 +20,7 @@
#include <errno.h>
#include <fcntl.h>
#include <fts.h>
+#include <glob.h>
#include <linux/loop.h>
#include <linux/module.h>
#include <mntent.h>
@@ -50,9 +51,9 @@
#include <android-base/unique_fd.h>
#include <bootloader_message/bootloader_message.h>
#include <cutils/android_reboot.h>
-#include <ext4_utils/ext4_crypt.h>
-#include <ext4_utils/ext4_crypt_init_extensions.h>
#include <fs_mgr.h>
+#include <fscrypt/fscrypt.h>
+#include <fscrypt/fscrypt_init_extensions.h>
#include <selinux/android.h>
#include <selinux/label.h>
#include <selinux/selinux.h>
@@ -307,8 +308,8 @@
}
}
- if (e4crypt_is_native()) {
- if (e4crypt_set_directory_policy(args[1].c_str())) {
+ if (fscrypt_is_native()) {
+ if (fscrypt_set_directory_policy(args[1].c_str())) {
return reboot_into_recovery(
{"--prompt_and_wipe_data", "--reason=set_policy_failed:"s + args[1]});
}
@@ -517,8 +518,8 @@
return reboot_into_recovery(options);
/* If reboot worked, there is no return. */
} else if (code == FS_MGR_MNTALL_DEV_FILE_ENCRYPTED) {
- if (e4crypt_install_keyring()) {
- return Error() << "e4crypt_install_keyring() failed";
+ if (fscrypt_install_keyring()) {
+ return Error() << "fscrypt_install_keyring() failed";
}
property_set("ro.crypto.state", "encrypted");
property_set("ro.crypto.type", "file");
@@ -528,8 +529,8 @@
ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
return Success();
} else if (code == FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED) {
- if (e4crypt_install_keyring()) {
- return Error() << "e4crypt_install_keyring() failed";
+ if (fscrypt_install_keyring()) {
+ return Error() << "fscrypt_install_keyring() failed";
}
property_set("ro.crypto.state", "encrypted");
property_set("ro.crypto.type", "file");
@@ -539,8 +540,8 @@
ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
return Success();
} else if (code == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) {
- if (e4crypt_install_keyring()) {
- return Error() << "e4crypt_install_keyring() failed";
+ if (fscrypt_install_keyring()) {
+ return Error() << "fscrypt_install_keyring() failed";
}
property_set("ro.crypto.state", "encrypted");
property_set("ro.crypto.type", "file");
@@ -1016,7 +1017,7 @@
}
service->AddReapCallback([reboot_reason](const siginfo_t& siginfo) {
if (siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) {
- if (e4crypt_is_native()) {
+ if (fscrypt_is_native()) {
LOG(ERROR) << "Rebooting into recovery, reason: " << reboot_reason;
if (auto result = reboot_into_recovery(
{"--prompt_and_wipe_data", "--reason="s + reboot_reason});
@@ -1038,7 +1039,7 @@
static Result<Success> do_installkey(const BuiltinArguments& args) {
if (!is_file_crypto()) return Success();
- auto unencrypted_dir = args[1] + e4crypt_unencrypted_folder;
+ auto unencrypted_dir = args[1] + fscrypt_unencrypted_folder;
if (!make_dir(unencrypted_dir, 0700) && errno != EEXIST) {
return ErrnoError() << "Failed to create " << unencrypted_dir;
}
@@ -1053,6 +1054,40 @@
{{"exec", "/system/bin/vdc", "--wait", "cryptfs", "init_user0"}, args.context});
}
+static Result<Success> do_parse_apex_configs(const BuiltinArguments& args) {
+ glob_t glob_result;
+ // @ is added to filter out the later paths, which are bind mounts of the places
+ // where the APEXes are really mounted at. Otherwise, we will parse the
+ // same file twice.
+ static constexpr char glob_pattern[] = "/apex/*@*/etc/*.rc";
+ const int ret = glob(glob_pattern, GLOB_MARK, nullptr, &glob_result);
+ if (ret != 0 && ret != GLOB_NOMATCH) {
+ globfree(&glob_result);
+ return Error() << "glob pattern '" << glob_pattern << "' failed";
+ }
+ std::vector<std::string> configs;
+ Parser parser = CreateServiceOnlyParser(ServiceList::GetInstance());
+ for (size_t i = 0; i < glob_result.gl_pathc; i++) {
+ configs.emplace_back(glob_result.gl_pathv[i]);
+ }
+ globfree(&glob_result);
+
+ bool success = true;
+ for (const auto& c : configs) {
+ if (c.back() == '/') {
+ // skip if directory
+ continue;
+ }
+ success &= parser.ParseConfigFile(c);
+ }
+ ServiceList::GetInstance().MarkServicesUpdate();
+ if (success) {
+ return Success();
+ } else {
+ return Error() << "Could not parse apex configs";
+ }
+}
+
// Builtin-function-map start
const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
@@ -1090,6 +1125,7 @@
// mount and umount are run in the same context as mount_all for symmetry.
{"mount_all", {1, kMax, {false, do_mount_all}}},
{"mount", {3, kMax, {false, do_mount}}},
+ {"parse_apex_configs", {0, 0, {false, do_parse_apex_configs}}},
{"umount", {1, 1, {false, do_umount}}},
{"readahead", {1, 2, {true, do_readahead}}},
{"restart", {1, 1, {false, do_restart}}},
diff --git a/init/devices.cpp b/init/devices.cpp
index 58c8b2e..45b17a2 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -419,7 +419,7 @@
}
void DeviceHandler::ColdbootDone() {
- skip_restorecon_ = true;
+ skip_restorecon_ = false;
}
DeviceHandler::DeviceHandler(std::vector<Permissions> dev_permissions,
diff --git a/init/devices_test.cpp b/init/devices_test.cpp
index d658f4d..3e7c1a8 100644
--- a/init/devices_test.cpp
+++ b/init/devices_test.cpp
@@ -16,8 +16,8 @@
#include "devices.h"
+#include <android-base/file.h>
#include <android-base/scopeguard.h>
-#include <android-base/test_utils.h>
#include <gtest/gtest.h>
#include "util.h"
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index eb86eb0..43b2ebf 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -17,6 +17,7 @@
#include "first_stage_mount.h"
#include <stdlib.h>
+#include <sys/mount.h>
#include <unistd.h>
#include <chrono>
@@ -33,7 +34,7 @@
#include <fs_mgr_avb.h>
#include <fs_mgr_dm_linear.h>
#include <fs_mgr_overlayfs.h>
-#include <liblp/metadata_format.h>
+#include <liblp/liblp.h>
#include "devices.h"
#include "switch_root.h"
@@ -69,9 +70,10 @@
bool MountPartition(fstab_rec* fstab_rec);
bool MountPartitions();
bool IsDmLinearEnabled();
- bool GetBackingDmLinearDevices();
+ bool GetDmLinearMetadataDevice();
+ bool InitDmLinearBackingDevices(const android::fs_mgr::LpMetadata& metadata);
- virtual ListenerAction UeventCallback(const Uevent& uevent);
+ ListenerAction UeventCallback(const Uevent& uevent);
// Pure virtual functions.
virtual bool GetDmVerityDevices() = 0;
@@ -106,14 +108,12 @@
~FirstStageMountVBootV2() override = default;
protected:
- ListenerAction UeventCallback(const Uevent& uevent) override;
bool GetDmVerityDevices() override;
bool SetUpDmVerity(fstab_rec* fstab_rec) override;
bool InitAvbHandle();
std::string device_tree_vbmeta_parts_;
FsManagerAvbUniquePtr avb_handle_;
- ByNameSymlinkMap by_name_symlink_map_;
};
// Static Functions
@@ -122,18 +122,8 @@
return is_android_dt_value_expected("vbmeta/compatible", "android,vbmeta");
}
-static bool ForceNormalBoot() {
- static bool force_normal_boot = []() {
- std::string cmdline;
- android::base::ReadFileToString("/proc/cmdline", &cmdline);
- return cmdline.find("androidboot.force_normal_boot=1") != std::string::npos;
- }();
-
- return force_normal_boot;
-}
-
static bool IsRecoveryMode() {
- return !ForceNormalBoot() && access("/system/bin/recovery", F_OK) == 0;
+ return access("/system/bin/recovery", F_OK) == 0;
}
// Class Definitions
@@ -183,7 +173,7 @@
}
bool FirstStageMount::InitDevices() {
- return GetBackingDmLinearDevices() && GetDmVerityDevices() && InitRequiredDevices();
+ return GetDmLinearMetadataDevice() && GetDmVerityDevices() && InitRequiredDevices();
}
bool FirstStageMount::IsDmLinearEnabled() {
@@ -193,7 +183,7 @@
return false;
}
-bool FirstStageMount::GetBackingDmLinearDevices() {
+bool FirstStageMount::GetDmLinearMetadataDevice() {
// Add any additional devices required for dm-linear mappings.
if (!IsDmLinearEnabled()) {
return true;
@@ -258,17 +248,48 @@
return true;
}
+bool FirstStageMount::InitDmLinearBackingDevices(const android::fs_mgr::LpMetadata& metadata) {
+ auto partition_names = GetBlockDevicePartitionNames(metadata);
+ for (const auto& partition_name : partition_names) {
+ if (partition_name == lp_metadata_partition_) {
+ continue;
+ }
+ required_devices_partition_names_.emplace(partition_name);
+ }
+ if (required_devices_partition_names_.empty()) {
+ return true;
+ }
+
+ auto uevent_callback = [this](const Uevent& uevent) { return UeventCallback(uevent); };
+ uevent_listener_.RegenerateUevents(uevent_callback);
+
+ if (!required_devices_partition_names_.empty()) {
+ LOG(ERROR) << __PRETTY_FUNCTION__ << ": partition(s) not found after polling timeout: "
+ << android::base::Join(required_devices_partition_names_, ", ");
+ return false;
+ }
+ return true;
+}
+
bool FirstStageMount::CreateLogicalPartitions() {
if (!IsDmLinearEnabled()) {
return true;
}
-
if (lp_metadata_partition_.empty()) {
LOG(ERROR) << "Could not locate logical partition tables in partition "
<< super_partition_name_;
return false;
}
- return android::fs_mgr::CreateLogicalPartitions(lp_metadata_partition_);
+
+ auto metadata = android::fs_mgr::ReadCurrentMetadata(lp_metadata_partition_);
+ if (!metadata) {
+ LOG(ERROR) << "Could not read logical partition metadata from " << lp_metadata_partition_;
+ return false;
+ }
+ if (!InitDmLinearBackingDevices(*metadata.get())) {
+ return false;
+ }
+ return android::fs_mgr::CreateLogicalPartitions(*metadata.get());
}
ListenerAction FirstStageMount::HandleBlockDevice(const std::string& name, const Uevent& uevent) {
@@ -371,11 +392,6 @@
[](const auto& rec) { return rec->mount_point == "/system"s; });
if (system_partition != mount_fstab_recs_.end()) {
- if (ForceNormalBoot()) {
- free((*system_partition)->mount_point);
- (*system_partition)->mount_point = strdup("/system_recovery_mount");
- }
-
if (!MountPartition(*system_partition)) {
return false;
}
@@ -526,33 +542,6 @@
return true;
}
-ListenerAction FirstStageMountVBootV2::UeventCallback(const Uevent& uevent) {
- // Check if this uevent corresponds to one of the required partitions and store its symlinks if
- // so, in order to create FsManagerAvbHandle later.
- // Note that the parent callback removes partitions from the list of required partitions
- // as it finds them, so this must happen first.
- if (!uevent.partition_name.empty() &&
- required_devices_partition_names_.find(uevent.partition_name) !=
- required_devices_partition_names_.end()) {
- // GetBlockDeviceSymlinks() will return three symlinks at most, depending on
- // the content of uevent. by-name symlink will be at [0] if uevent->partition_name
- // is not empty. e.g.,
- // - /dev/block/platform/soc.0/f9824900.sdhci/by-name/modem
- // - /dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1
- std::vector<std::string> links = device_handler_->GetBlockDeviceSymlinks(uevent);
- if (!links.empty()) {
- auto [it, inserted] = by_name_symlink_map_.emplace(uevent.partition_name, links[0]);
- if (!inserted) {
- LOG(ERROR) << "Partition '" << uevent.partition_name
- << "' already existed in the by-name symlink map with a value of '"
- << it->second << "', new value '" << links[0] << "' will be ignored.";
- }
- }
- }
-
- return FirstStageMount::UeventCallback(uevent);
-}
-
bool FirstStageMountVBootV2::SetUpDmVerity(fstab_rec* fstab_rec) {
if (fs_mgr_is_avb(fstab_rec)) {
if (!InitAvbHandle()) return false;
@@ -576,13 +565,7 @@
bool FirstStageMountVBootV2::InitAvbHandle() {
if (avb_handle_) return true; // Returns true if the handle is already initialized.
- if (by_name_symlink_map_.empty()) {
- LOG(ERROR) << "by_name_symlink_map_ is empty";
- return false;
- }
-
- avb_handle_ = FsManagerAvbHandle::Open(std::move(by_name_symlink_map_));
- by_name_symlink_map_.clear(); // Removes all elements after the above std::move().
+ avb_handle_ = FsManagerAvbHandle::Open();
if (!avb_handle_) {
PLOG(ERROR) << "Failed to open FsManagerAvbHandle";
@@ -633,8 +616,7 @@
return;
}
- FsManagerAvbUniquePtr avb_handle =
- FsManagerAvbHandle::Open(std::move(avb_first_mount.by_name_symlink_map_));
+ FsManagerAvbUniquePtr avb_handle = FsManagerAvbHandle::Open();
if (!avb_handle) {
PLOG(ERROR) << "Failed to open FsManagerAvbHandle for INIT_AVB_VERSION";
return;
diff --git a/init/grab-bootchart.sh b/init/grab-bootchart.sh
index c4ff6df..2c56698 100755
--- a/init/grab-bootchart.sh
+++ b/init/grab-bootchart.sh
@@ -17,6 +17,6 @@
adb "${@}" pull $LOGROOT/$f $TMPDIR/$f 2>&1 > /dev/null
done
(cd $TMPDIR && tar -czf $TARBALL $FILES)
-bootchart ${TMPDIR}/${TARBALL}
-gnome-open ${TARBALL%.tgz}.png
+pybootchartgui ${TMPDIR}/${TARBALL}
+xdg-open ${TARBALL%.tgz}.png
echo "Clean up ${TMPDIR}/ and ./${TARBALL%.tgz}.png when done"
diff --git a/init/init.cpp b/init/init.cpp
index b12ba8c..e7dbc11 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -39,6 +39,7 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <cutils/android_reboot.h>
+#include <fs_mgr_vendor_overlay.h>
#include <keyutils.h>
#include <libavb/libavb.h>
#include <selinux/android.h>
@@ -131,6 +132,14 @@
return parser;
}
+// parser that only accepts new services
+Parser CreateServiceOnlyParser(ServiceList& service_list) {
+ Parser parser;
+
+ parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, subcontexts));
+ return parser;
+}
+
static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
Parser parser = CreateParser(action_manager, service_list);
@@ -717,6 +726,7 @@
InstallSignalFdHandler(&epoll);
property_load_boot_defaults();
+ fs_mgr_vendor_overlay_mount_all();
export_oem_lock_status();
StartPropertyService(&epoll);
set_usb_controller();
diff --git a/init/init.h b/init/init.h
index f244ad7..65405aa 100644
--- a/init/init.h
+++ b/init/init.h
@@ -38,6 +38,7 @@
extern std::vector<std::string> late_import_paths;
Parser CreateParser(ActionManager& action_manager, ServiceList& service_list);
+Parser CreateServiceOnlyParser(ServiceList& service_list);
void HandleControlMessage(const std::string& msg, const std::string& arg, pid_t pid);
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
index d81ca5c..c2c6868 100644
--- a/init/init_first_stage.cpp
+++ b/init/init_first_stage.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <dirent.h>
+#include <fcntl.h>
#include <paths.h>
#include <stdlib.h>
#include <sys/mount.h>
@@ -26,19 +28,72 @@
#include <vector>
#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <cutils/android_reboot.h>
#include <private/android_filesystem_config.h>
#include "first_stage_mount.h"
#include "reboot_utils.h"
+#include "switch_root.h"
#include "util.h"
using android::base::boot_clock;
+using namespace std::literals;
+
namespace android {
namespace init {
+namespace {
+
+void FreeRamdisk(DIR* dir, dev_t dev) {
+ int dfd = dirfd(dir);
+
+ dirent* de;
+ while ((de = readdir(dir)) != nullptr) {
+ if (de->d_name == "."s || de->d_name == ".."s) {
+ continue;
+ }
+
+ bool is_dir = false;
+
+ if (de->d_type == DT_DIR || de->d_type == DT_UNKNOWN) {
+ struct stat info;
+ if (fstatat(dfd, de->d_name, &info, AT_SYMLINK_NOFOLLOW) != 0) {
+ continue;
+ }
+
+ if (info.st_dev != dev) {
+ continue;
+ }
+
+ if (S_ISDIR(info.st_mode)) {
+ is_dir = true;
+ auto fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
+ if (fd >= 0) {
+ auto subdir =
+ std::unique_ptr<DIR, decltype(&closedir)>{fdopendir(fd), closedir};
+ if (subdir) {
+ FreeRamdisk(subdir.get(), dev);
+ } else {
+ close(fd);
+ }
+ }
+ }
+ }
+ unlinkat(dfd, de->d_name, is_dir ? AT_REMOVEDIR : 0);
+ }
+}
+
+bool ForceNormalBoot() {
+ std::string cmdline;
+ android::base::ReadFileToString("/proc/cmdline", &cmdline);
+ return cmdline.find("androidboot.force_normal_boot=1") != std::string::npos;
+}
+
+} // namespace
+
int main(int argc, char** argv) {
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
@@ -117,10 +172,41 @@
LOG(INFO) << "init first stage started!";
+ auto old_root_dir = std::unique_ptr<DIR, decltype(&closedir)>{opendir("/"), closedir};
+ if (!old_root_dir) {
+ PLOG(ERROR) << "Could not opendir(\"/\"), not freeing ramdisk";
+ }
+
+ struct stat old_root_info;
+ if (stat("/", &old_root_info) != 0) {
+ PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk";
+ old_root_dir.reset();
+ }
+
+ if (ForceNormalBoot()) {
+ mkdir("/first_stage_ramdisk", 0755);
+ // SwitchRoot() must be called with a mount point as the target, so we bind mount the
+ // target directory to itself here.
+ if (mount("/first_stage_ramdisk", "/first_stage_ramdisk", nullptr, MS_BIND, nullptr) != 0) {
+ LOG(FATAL) << "Could not bind mount /first_stage_ramdisk to itself";
+ }
+ SwitchRoot("/first_stage_ramdisk");
+ }
+
if (!DoFirstStageMount()) {
LOG(FATAL) << "Failed to mount required partitions early ...";
}
+ struct stat new_root_info;
+ if (stat("/", &new_root_info) != 0) {
+ PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk";
+ old_root_dir.reset();
+ }
+
+ if (old_root_dir && old_root_info.st_dev != new_root_info.st_dev) {
+ FreeRamdisk(old_root_dir.get(), old_root_info.st_dev);
+ }
+
SetInitAvbVersionInRecovery();
static constexpr uint32_t kNanosecondsPerMillisecond = 1e6;
diff --git a/init/init_test.cpp b/init/init_test.cpp
index 0f9635f..c2f0c41 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -17,7 +17,6 @@
#include <functional>
#include <android-base/file.h>
-#include <android-base/test_utils.h>
#include <gtest/gtest.h>
#include "action.h"
diff --git a/init/parser.h b/init/parser.h
index 2454b6a..f30bda7 100644
--- a/init/parser.h
+++ b/init/parser.h
@@ -72,6 +72,7 @@
Parser();
bool ParseConfig(const std::string& path);
+ bool ParseConfigFile(const std::string& path);
void AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser);
void AddSingleLineParser(const std::string& prefix, LineCallback callback);
@@ -82,7 +83,6 @@
private:
void ParseData(const std::string& filename, std::string* data);
- bool ParseConfigFile(const std::string& path);
bool ParseConfigDir(const std::string& path);
std::map<std::string, std::unique_ptr<SectionParser>> section_parsers_;
diff --git a/init/persistent_properties_test.cpp b/init/persistent_properties_test.cpp
index 872e9a1..13796a6 100644
--- a/init/persistent_properties_test.cpp
+++ b/init/persistent_properties_test.cpp
@@ -20,7 +20,7 @@
#include <vector>
-#include <android-base/test_utils.h>
+#include <android-base/file.h>
#include <gtest/gtest.h>
#include "util.h"
diff --git a/init/reboot.cpp b/init/reboot.cpp
index a145797..45dc6d3 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -468,7 +468,7 @@
// adb reboot fastboot should boot into bootloader for devices not
// supporting logical partitions.
if (reboot_target == "fastboot" &&
- !android::base::GetBoolProperty("ro.boot.logical_partitions", false)) {
+ !android::base::GetBoolProperty("ro.boot.dynamic_partitions", false)) {
reboot_target = "bootloader";
}
// When rebooting to the bootloader notify the bootloader writing
diff --git a/init/service.cpp b/init/service.cpp
index 7f49423..5aa3764 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -235,9 +235,6 @@
ioprio_pri_(0),
priority_(0),
oom_score_adjust_(-1000),
- swappiness_(-1),
- soft_limit_in_bytes_(-1),
- limit_in_bytes_(-1),
start_order_(0),
args_(args) {}
@@ -630,6 +627,18 @@
return Success();
}
+Result<Success> Service::ParseMemcgLimitPercent(std::vector<std::string>&& args) {
+ if (!ParseInt(args[1], &limit_percent_, 0)) {
+ return Error() << "limit_percent value must be equal or greater than 0";
+ }
+ return Success();
+}
+
+Result<Success> Service::ParseMemcgLimitProperty(std::vector<std::string>&& args) {
+ limit_property_ = std::move(args[1]);
+ return Success();
+}
+
Result<Success> Service::ParseMemcgSoftLimitInBytes(std::vector<std::string>&& args) {
if (!ParseInt(args[1], &soft_limit_in_bytes_, 0)) {
return Error() << "soft_limit_in_bytes value must be equal or greater than 0";
@@ -756,6 +765,11 @@
return Success();
}
+Result<Success> Service::ParseUpdatable(std::vector<std::string>&& args) {
+ updatable_ = true;
+ return Success();
+}
+
class Service::OptionParserMap : public KeywordMap<OptionParser> {
public:
OptionParserMap() {}
@@ -783,6 +797,10 @@
{"keycodes", {1, kMax, &Service::ParseKeycodes}},
{"memcg.limit_in_bytes",
{1, 1, &Service::ParseMemcgLimitInBytes}},
+ {"memcg.limit_percent",
+ {1, 1, &Service::ParseMemcgLimitPercent}},
+ {"memcg.limit_property",
+ {1, 1, &Service::ParseMemcgLimitProperty}},
{"memcg.soft_limit_in_bytes",
{1, 1, &Service::ParseMemcgSoftLimitInBytes}},
{"memcg.swappiness",
@@ -804,6 +822,7 @@
{"socket", {3, 6, &Service::ParseSocket}},
{"timeout_period",
{1, 1, &Service::ParseTimeoutPeriod}},
+ {"updatable", {0, 0, &Service::ParseUpdatable}},
{"user", {1, 1, &Service::ParseUser}},
{"writepid", {1, kMax, &Service::ParseWritepid}},
};
@@ -821,6 +840,13 @@
}
Result<Success> Service::ExecStart() {
+ if (is_updatable() && !ServiceList::GetInstance().IsServicesUpdated()) {
+ // Don't delay the service for ExecStart() as the semantic is that
+ // the caller might depend on the side effect of the execution.
+ return Error() << "Cannot start an updatable service '" << name_
+ << "' before configs from APEXes are all loaded";
+ }
+
flags_ |= SVC_ONESHOT;
if (auto result = Start(); !result) {
@@ -838,6 +864,13 @@
}
Result<Success> Service::Start() {
+ if (is_updatable() && !ServiceList::GetInstance().IsServicesUpdated()) {
+ ServiceList::GetInstance().DelayService(*this);
+ return Error() << "Cannot start an updatable service '" << name_
+ << "' before configs from APEXes are all loaded. "
+ << "Queued for execution.";
+ }
+
bool disabled = (flags_ & (SVC_DISABLED | SVC_RESET));
// Starting a service removes it from the disabled or reset state and
// immediately takes it out of the restarting state if it was in there.
@@ -1001,11 +1034,13 @@
start_order_ = next_start_order_++;
process_cgroup_empty_ = false;
- errno = -createProcessGroup(uid_, pid_);
+ bool use_memcg = swappiness_ != -1 || soft_limit_in_bytes_ != -1 || limit_in_bytes_ != -1 ||
+ limit_percent_ != -1 || !limit_property_.empty();
+ errno = -createProcessGroup(uid_, pid_, use_memcg);
if (errno != 0) {
PLOG(ERROR) << "createProcessGroup(" << uid_ << ", " << pid_ << ") failed for service '"
<< name_ << "'";
- } else {
+ } else if (use_memcg) {
if (swappiness_ != -1) {
if (!setProcessGroupSwappiness(uid_, pid_, swappiness_)) {
PLOG(ERROR) << "setProcessGroupSwappiness failed";
@@ -1018,8 +1053,29 @@
}
}
- if (limit_in_bytes_ != -1) {
- if (!setProcessGroupLimit(uid_, pid_, limit_in_bytes_)) {
+ size_t computed_limit_in_bytes = limit_in_bytes_;
+ if (limit_percent_ != -1) {
+ long page_size = sysconf(_SC_PAGESIZE);
+ long num_pages = sysconf(_SC_PHYS_PAGES);
+ if (page_size > 0 && num_pages > 0) {
+ size_t max_mem = SIZE_MAX;
+ if (size_t(num_pages) < SIZE_MAX / size_t(page_size)) {
+ max_mem = size_t(num_pages) * size_t(page_size);
+ }
+ computed_limit_in_bytes =
+ std::min(computed_limit_in_bytes, max_mem / 100 * limit_percent_);
+ }
+ }
+
+ if (!limit_property_.empty()) {
+ // This ends up overwriting computed_limit_in_bytes but only if the
+ // property is defined.
+ computed_limit_in_bytes = android::base::GetUintProperty(
+ limit_property_, computed_limit_in_bytes, SIZE_MAX);
+ }
+
+ if (computed_limit_in_bytes != size_t(-1)) {
+ if (!setProcessGroupLimit(uid_, pid_, computed_limit_in_bytes)) {
PLOG(ERROR) << "setProcessGroupLimit failed";
}
}
@@ -1244,6 +1300,32 @@
}
}
+void ServiceList::MarkServicesUpdate() {
+ services_update_finished_ = true;
+
+ // start the delayed services
+ for (const auto& name : delayed_service_names_) {
+ Service* service = FindService(name);
+ if (service == nullptr) {
+ LOG(ERROR) << "delayed service '" << name << "' could not be found.";
+ continue;
+ }
+ if (auto result = service->Start(); !result) {
+ LOG(ERROR) << result.error_string();
+ }
+ }
+ delayed_service_names_.clear();
+}
+
+void ServiceList::DelayService(const Service& service) {
+ if (services_update_finished_) {
+ LOG(ERROR) << "Cannot delay the start of service '" << service.name()
+ << "' because all services are already updated. Ignoring.";
+ return;
+ }
+ delayed_service_names_.emplace_back(service.name());
+}
+
Result<Success> ServiceParser::ParseSection(std::vector<std::string>&& args,
const std::string& filename, int line) {
if (args.size() < 3) {
@@ -1255,6 +1337,8 @@
return Error() << "invalid service name '" << name << "'";
}
+ filename_ = filename;
+
Subcontext* restart_action_subcontext = nullptr;
if (subcontexts_) {
for (auto& subcontext : *subcontexts_) {
@@ -1290,6 +1374,11 @@
<< "'";
}
+ if (StartsWith(filename_, "/apex/") && !old_service->is_updatable()) {
+ return Error() << "cannot update a non-updatable service '" << service_->name()
+ << "' with a config in APEX";
+ }
+
service_list_->RemoveService(*old_service);
old_service = nullptr;
}
diff --git a/init/service.h b/init/service.h
index c7beee9..56e75b0 100644
--- a/init/service.h
+++ b/init/service.h
@@ -123,6 +123,7 @@
std::chrono::seconds restart_period() const { return restart_period_; }
std::optional<std::chrono::seconds> timeout_period() const { return timeout_period_; }
const std::vector<std::string>& args() const { return args_; }
+ bool is_updatable() const { return updatable_; }
private:
using OptionParser = Result<Success> (Service::*)(std::vector<std::string>&& args);
@@ -154,6 +155,8 @@
Result<Success> ParseOomScoreAdjust(std::vector<std::string>&& args);
Result<Success> ParseOverride(std::vector<std::string>&& args);
Result<Success> ParseMemcgLimitInBytes(std::vector<std::string>&& args);
+ Result<Success> ParseMemcgLimitPercent(std::vector<std::string>&& args);
+ Result<Success> ParseMemcgLimitProperty(std::vector<std::string>&& args);
Result<Success> ParseMemcgSoftLimitInBytes(std::vector<std::string>&& args);
Result<Success> ParseMemcgSwappiness(std::vector<std::string>&& args);
Result<Success> ParseNamespace(std::vector<std::string>&& args);
@@ -168,6 +171,7 @@
Result<Success> ParseFile(std::vector<std::string>&& args);
Result<Success> ParseUser(std::vector<std::string>&& args);
Result<Success> ParseWritepid(std::vector<std::string>&& args);
+ Result<Success> ParseUpdatable(std::vector<std::string>&& args);
template <typename T>
Result<Success> AddDescriptor(std::vector<std::string>&& args);
@@ -213,9 +217,12 @@
int oom_score_adjust_;
- int swappiness_;
- int soft_limit_in_bytes_;
- int limit_in_bytes_;
+ int swappiness_ = -1;
+ int soft_limit_in_bytes_ = -1;
+
+ int limit_in_bytes_ = -1;
+ int limit_percent_ = -1;
+ std::string limit_property_;
bool process_cgroup_empty_ = false;
@@ -230,6 +237,8 @@
std::chrono::seconds restart_period_ = 5s;
std::optional<std::chrono::seconds> timeout_period_;
+ bool updatable_ = false;
+
std::vector<std::string> args_;
std::vector<std::function<void(const siginfo_t& siginfo)>> reap_callbacks_;
@@ -274,8 +283,15 @@
const std::vector<std::unique_ptr<Service>>& services() const { return services_; }
const std::vector<Service*> services_in_shutdown_order() const;
+ void MarkServicesUpdate();
+ bool IsServicesUpdated() const { return services_update_finished_; }
+ void DelayService(const Service& service);
+
private:
std::vector<std::unique_ptr<Service>> services_;
+
+ bool services_update_finished_ = false;
+ std::vector<std::string> delayed_service_names_;
};
class ServiceParser : public SectionParser {
@@ -286,6 +302,7 @@
int line) override;
Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
Result<Success> EndSection() override;
+ void EndFile() override { filename_ = ""; }
private:
bool IsValidName(const std::string& name) const;
@@ -293,6 +310,7 @@
ServiceList* service_list_;
std::vector<Subcontext>* subcontexts_;
std::unique_ptr<Service> service_;
+ std::string filename_;
};
} // namespace init
diff --git a/init/switch_root.cpp b/init/switch_root.cpp
index 0e59b57..575b67f 100644
--- a/init/switch_root.cpp
+++ b/init/switch_root.cpp
@@ -16,7 +16,6 @@
#include "switch_root.h"
-#include <dirent.h>
#include <fcntl.h>
#include <mntent.h>
#include <sys/mount.h>
@@ -35,45 +34,6 @@
namespace {
-void FreeRamdisk(DIR* dir, dev_t dev) {
- int dfd = dirfd(dir);
-
- dirent* de;
- while ((de = readdir(dir)) != nullptr) {
- if (de->d_name == "."s || de->d_name == ".."s) {
- continue;
- }
-
- bool is_dir = false;
-
- if (de->d_type == DT_DIR || de->d_type == DT_UNKNOWN) {
- struct stat info;
- if (fstatat(dfd, de->d_name, &info, AT_SYMLINK_NOFOLLOW) != 0) {
- continue;
- }
-
- if (info.st_dev != dev) {
- continue;
- }
-
- if (S_ISDIR(info.st_mode)) {
- is_dir = true;
- auto fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
- if (fd >= 0) {
- auto subdir =
- std::unique_ptr<DIR, decltype(&closedir)>{fdopendir(fd), closedir};
- if (subdir) {
- FreeRamdisk(subdir.get(), dev);
- } else {
- close(fd);
- }
- }
- }
- }
- unlinkat(dfd, de->d_name, is_dir ? AT_REMOVEDIR : 0);
- }
-}
-
std::vector<std::string> GetMounts(const std::string& new_root) {
auto fp = std::unique_ptr<std::FILE, decltype(&endmntent)>{setmntent("/proc/mounts", "re"),
endmntent};
@@ -112,24 +72,16 @@
void SwitchRoot(const std::string& new_root) {
auto mounts = GetMounts(new_root);
+ LOG(INFO) << "Switching root to '" << new_root << "'";
+
for (const auto& mount_path : mounts) {
auto new_mount_path = new_root + mount_path;
+ mkdir(new_mount_path.c_str(), 0755);
if (mount(mount_path.c_str(), new_mount_path.c_str(), nullptr, MS_MOVE, nullptr) != 0) {
PLOG(FATAL) << "Unable to move mount at '" << mount_path << "'";
}
}
- auto old_root_dir = std::unique_ptr<DIR, decltype(&closedir)>{opendir("/"), closedir};
- if (!old_root_dir) {
- PLOG(ERROR) << "Could not opendir(\"/\"), not freeing ramdisk";
- }
-
- struct stat old_root_info;
- if (stat("/", &old_root_info) != 0) {
- PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk";
- old_root_dir.reset();
- }
-
if (chdir(new_root.c_str()) != 0) {
PLOG(FATAL) << "Could not chdir to new_root, '" << new_root << "'";
}
@@ -141,10 +93,6 @@
if (chroot(".") != 0) {
PLOG(FATAL) << "Unable to chroot to new root";
}
-
- if (old_root_dir) {
- FreeRamdisk(old_root_dir.get(), old_root_info.st_dev);
- }
}
} // namespace init
diff --git a/init/ueventd_parser_test.cpp b/init/ueventd_parser_test.cpp
index 31208b9..c3af341 100644
--- a/init/ueventd_parser_test.cpp
+++ b/init/ueventd_parser_test.cpp
@@ -16,7 +16,7 @@
#include "ueventd_parser.h"
-#include <android-base/test_utils.h>
+#include <android-base/file.h>
#include <gtest/gtest.h>
#include <private/android_filesystem_config.h>
diff --git a/init/ueventd_test.cpp b/init/ueventd_test.cpp
index 7290051..bfdc28e 100644
--- a/init/ueventd_test.cpp
+++ b/init/ueventd_test.cpp
@@ -27,7 +27,6 @@
#include <android-base/file.h>
#include <android-base/scopeguard.h>
-#include <android-base/test_utils.h>
#include <gtest/gtest.h>
#include <selinux/android.h>
#include <selinux/label.h>
diff --git a/init/util_test.cpp b/init/util_test.cpp
index 3ae53a4..1b5afba 100644
--- a/init/util_test.cpp
+++ b/init/util_test.cpp
@@ -20,8 +20,8 @@
#include <fcntl.h>
#include <sys/stat.h>
+#include <android-base/file.h>
#include <android-base/stringprintf.h>
-#include <android-base/test_utils.h>
#include <gtest/gtest.h>
using namespace std::literals::string_literals;
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
index 4e7f761..fe28eba 100644
--- a/libbacktrace/UnwindStack.cpp
+++ b/libbacktrace/UnwindStack.cpp
@@ -49,6 +49,7 @@
unwindstack::Unwinder unwinder(MAX_BACKTRACE_FRAMES + num_ignore_frames, stack_map->stack_maps(),
regs, stack_map->process_memory());
unwinder.SetResolveNames(stack_map->ResolveNames());
+ stack_map->SetArch(regs->Arch());
if (stack_map->GetJitDebug() != nullptr) {
unwinder.SetJitDebug(stack_map->GetJitDebug(), regs->Arch());
}
diff --git a/libbacktrace/UnwindStackMap.cpp b/libbacktrace/UnwindStackMap.cpp
index 52dd441..9d15af2 100644
--- a/libbacktrace/UnwindStackMap.cpp
+++ b/libbacktrace/UnwindStackMap.cpp
@@ -25,6 +25,7 @@
#include <unwindstack/Elf.h>
#include <unwindstack/MapInfo.h>
#include <unwindstack/Maps.h>
+#include <unwindstack/Regs.h>
#include "UnwindStackMap.h"
@@ -106,7 +107,17 @@
return "";
}
- unwindstack::Elf* elf = map_info->GetElf(process_memory());
+ if (arch_ == unwindstack::ARCH_UNKNOWN) {
+ if (pid_ == getpid()) {
+ arch_ = unwindstack::Regs::CurrentArch();
+ } else {
+ // Create a remote regs, to figure out the architecture.
+ std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::RemoteGet(pid_));
+ arch_ = regs->Arch();
+ }
+ }
+
+ unwindstack::Elf* elf = map_info->GetElf(process_memory(), arch_);
std::string name;
uint64_t func_offset;
diff --git a/libbacktrace/UnwindStackMap.h b/libbacktrace/UnwindStackMap.h
index 039f4a2..e19b605 100644
--- a/libbacktrace/UnwindStackMap.h
+++ b/libbacktrace/UnwindStackMap.h
@@ -30,6 +30,7 @@
#if !defined(NO_LIBDEXFILE_SUPPORT)
#include <unwindstack/DexFiles.h>
#endif
+#include <unwindstack/Elf.h>
#include <unwindstack/JitDebug.h>
#include <unwindstack/Maps.h>
@@ -58,6 +59,8 @@
unwindstack::DexFiles* GetDexFiles() { return dex_files_.get(); }
#endif
+ void SetArch(unwindstack::ArchEnum arch) { arch_ = arch; }
+
protected:
uint64_t GetLoadBias(size_t index) override;
@@ -67,6 +70,8 @@
#if !defined(NO_LIBDEXFILE_SUPPORT)
std::unique_ptr<unwindstack::DexFiles> dex_files_;
#endif
+
+ unwindstack::ArchEnum arch_ = unwindstack::ARCH_UNKNOWN;
};
class UnwindStackOfflineMap : public UnwindStackMap {
diff --git a/libcrypto_utils/tests/Android.bp b/libcrypto_utils/tests/Android.bp
new file mode 100644
index 0000000..5aadfe2
--- /dev/null
+++ b/libcrypto_utils/tests/Android.bp
@@ -0,0 +1,29 @@
+//
+// Copyright (C) 2016 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.
+//
+
+cc_test_host {
+ name: "libcrypto_utils_test",
+ srcs: ["android_pubkey_test.cpp"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+ shared_libs: [
+ "libcrypto_utils",
+ "libcrypto",
+ ],
+}
diff --git a/libcrypto_utils/tests/Android.mk b/libcrypto_utils/tests/Android.mk
deleted file mode 100644
index ef3d0cf..0000000
--- a/libcrypto_utils/tests/Android.mk
+++ /dev/null
@@ -1,24 +0,0 @@
-#
-# Copyright (C) 2016 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libcrypto_utils_test
-LOCAL_SRC_FILES := android_pubkey_test.cpp
-LOCAL_CFLAGS := -Wall -Werror -Wextra
-LOCAL_SHARED_LIBRARIES := libcrypto_utils libcrypto
-include $(BUILD_HOST_NATIVE_TEST)
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index bd5f26f..ee52f5e 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -60,7 +60,7 @@
// way up to the root.
static const struct fs_path_config android_dirs[] = {
- // clang-format off
+ // clang-format off
{ 00770, AID_SYSTEM, AID_CACHE, 0, "cache" },
{ 00555, AID_ROOT, AID_ROOT, 0, "config" },
{ 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app" },
@@ -80,17 +80,18 @@
{ 00775, AID_ROOT, AID_ROOT, 0, "data/preloads" },
{ 00771, AID_SYSTEM, AID_SYSTEM, 0, "data" },
{ 00755, AID_ROOT, AID_SYSTEM, 0, "mnt" },
- { 00755, AID_ROOT, AID_SHELL, 0, "product/bin" },
+ { 00751, AID_ROOT, AID_SHELL, 0, "product/bin" },
{ 00750, AID_ROOT, AID_SHELL, 0, "sbin" },
{ 00777, AID_ROOT, AID_ROOT, 0, "sdcard" },
{ 00751, AID_ROOT, AID_SDCARD_R, 0, "storage" },
- { 00755, AID_ROOT, AID_SHELL, 0, "system/bin" },
+ { 00751, AID_ROOT, AID_SHELL, 0, "system/bin" },
{ 00755, AID_ROOT, AID_ROOT, 0, "system/etc/ppp" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/vendor" },
- { 00755, AID_ROOT, AID_SHELL, 0, "system/xbin" },
+ { 00751, AID_ROOT, AID_SHELL, 0, "system/xbin" },
+ { 00751, AID_ROOT, AID_SHELL, 0, "vendor/bin" },
{ 00755, AID_ROOT, AID_SHELL, 0, "vendor" },
{ 00755, AID_ROOT, AID_ROOT, 0, 0 },
- // clang-format on
+ // clang-format on
};
#ifndef __ANDROID_VNDK__
auto __for_testing_only__android_dirs = android_dirs;
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index 845c586..68bf898 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -132,6 +132,7 @@
#define AID_LMKD 1069 /* low memory killer daemon */
#define AID_LLKD 1070 /* live lock daemon */
#define AID_IORAPD 1071 /* input/output readahead and pin daemon */
+#define AID_GPU_SERVICE 1072 /* GPU service daemon */
/* Changes to this file must be made in AOSP, *not* in internal branches. */
#define AID_SHELL 2000 /* adb and debug shell user */
diff --git a/libcutils/tests/android_get_control_file_test.cpp b/libcutils/tests/android_get_control_file_test.cpp
index 6c6fd2a..8de8530 100644
--- a/libcutils/tests/android_get_control_file_test.cpp
+++ b/libcutils/tests/android_get_control_file_test.cpp
@@ -23,8 +23,8 @@
#include <string>
+#include <android-base/file.h>
#include <android-base/stringprintf.h>
-#include <android-base/test_utils.h>
#include <cutils/android_get_control_file.h>
#include <gtest/gtest.h>
diff --git a/libcutils/tests/trace-dev_test.cpp b/libcutils/tests/trace-dev_test.cpp
index f8d4f00..832b36a 100644
--- a/libcutils/tests/trace-dev_test.cpp
+++ b/libcutils/tests/trace-dev_test.cpp
@@ -22,7 +22,6 @@
#include <android-base/file.h>
#include <android-base/stringprintf.h>
-#include <android-base/test_utils.h>
#include <gtest/gtest.h>
#include "../trace-dev.cpp"
diff --git a/liblog/Android.mk b/liblog/Android.mk
deleted file mode 100644
index 6c4dff5..0000000
--- a/liblog/Android.mk
+++ /dev/null
@@ -1,3 +0,0 @@
-LOCAL_PATH := $(my-dir)
-
-include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/liblog/include/android/log.h b/liblog/include/android/log.h
index ee9220d..b2f0ed9 100644
--- a/liblog/include/android/log.h
+++ b/liblog/include/android/log.h
@@ -14,25 +14,7 @@
* limitations under the License.
*/
-#ifndef _ANDROID_LOG_H
-#define _ANDROID_LOG_H
-
-/******************************************************************
- *
- * IMPORTANT NOTICE:
- *
- * This file is part of Android's set of stable system headers
- * exposed by the Android NDK (Native Development Kit) since
- * platform release 1.5
- *
- * Third-party source AND binary code relies on the definitions
- * here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES.
- *
- * - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES)
- * - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS
- * - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY
- * - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES
- */
+#pragma once
/**
* @addtogroup Logging
@@ -154,27 +136,51 @@
#ifndef log_id_t_defined
#define log_id_t_defined
+/**
+ * Identifies a specific log buffer for __android_log_buf_write()
+ * and __android_log_buf_print().
+ */
typedef enum log_id {
LOG_ID_MIN = 0,
+ /** The main log buffer. This is the only log buffer available to apps. */
LOG_ID_MAIN = 0,
+ /** The radio log buffer. */
LOG_ID_RADIO = 1,
+ /** The event log buffer. */
LOG_ID_EVENTS = 2,
+ /** The system log buffer. */
LOG_ID_SYSTEM = 3,
+ /** The crash log buffer. */
LOG_ID_CRASH = 4,
+ /** The statistics log buffer. */
LOG_ID_STATS = 5,
+ /** The security log buffer. */
LOG_ID_SECURITY = 6,
- LOG_ID_KERNEL = 7, /* place last, third-parties can not use it */
+ /** The kernel log buffer. */
+ LOG_ID_KERNEL = 7,
LOG_ID_MAX
} log_id_t;
#endif
-/*
- * Send a simple string to the log.
+/**
+ * Writes the constant string `text` to the log buffer `id`,
+ * with priority `prio` and tag `tag`.
+ *
+ * Apps should use __android_log_write() instead.
*/
int __android_log_buf_write(int bufID, int prio, const char* tag,
const char* text);
+
+/**
+ * Writes a formatted string to log buffer `id`,
+ * with priority `prio` and tag `tag`.
+ * The details of formatting are the same as for
+ * [printf(3)](http://man7.org/linux/man-pages/man3/printf.3.html).
+ *
+ * Apps should use __android_log_print() instead.
+ */
int __android_log_buf_print(int bufID, int prio, const char* tag,
const char* fmt, ...)
#if defined(__GNUC__)
@@ -187,5 +193,3 @@
#endif
/** @} */
-
-#endif /* _ANDROID_LOG_H */
diff --git a/liblog/pmsg_reader.c b/liblog/pmsg_reader.c
index c3ed8a2..bf0e4fe 100644
--- a/liblog/pmsg_reader.c
+++ b/liblog/pmsg_reader.c
@@ -269,6 +269,14 @@
}
}
+static void* realloc_or_free(void* ptr, size_t new_size) {
+ void* result = realloc(ptr, new_size);
+ if (!result) {
+ free(ptr);
+ }
+ return result;
+}
+
LIBLOG_ABI_PRIVATE ssize_t
__android_log_pmsg_file_read(log_id_t logId, char prio, const char* prefix,
__android_log_pmsg_file_read_fn fn, void* arg) {
@@ -541,7 +549,7 @@
/* Missing sequence numbers */
while (sequence < content->entry.nsec) {
/* plus space for enforced nul */
- buf = realloc(buf, len + sizeof(char) + sizeof(char));
+ buf = realloc_or_free(buf, len + sizeof(char) + sizeof(char));
if (!buf) {
break;
}
@@ -556,7 +564,7 @@
continue;
}
/* plus space for enforced nul */
- buf = realloc(buf, len + add_len + sizeof(char));
+ buf = realloc_or_free(buf, len + add_len + sizeof(char));
if (!buf) {
ret = -ENOMEM;
list_remove(content_node);
diff --git a/liblog/tests/Android.bp b/liblog/tests/Android.bp
new file mode 100644
index 0000000..e6a9c0c
--- /dev/null
+++ b/liblog/tests/Android.bp
@@ -0,0 +1,98 @@
+//
+// Copyright (C) 2013-2014 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.
+//
+
+// -----------------------------------------------------------------------------
+// Benchmarks.
+// -----------------------------------------------------------------------------
+
+// Build benchmarks for the device. Run with:
+// adb shell liblog-benchmarks
+cc_benchmark {
+ name: "liblog-benchmarks",
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-fno-builtin",
+ ],
+ shared_libs: [
+ "liblog",
+ "libm",
+ "libbase",
+ ],
+ srcs: ["liblog_benchmark.cpp"],
+}
+
+// -----------------------------------------------------------------------------
+// Unit tests.
+// -----------------------------------------------------------------------------
+
+cc_defaults {
+ name: "liblog-tests-defaults",
+
+ cflags: [
+ "-fstack-protector-all",
+ "-g",
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-fno-builtin",
+ ],
+ srcs: [
+ "libc_test.cpp",
+ "liblog_test_default.cpp",
+ "liblog_test_local.cpp",
+ "liblog_test_stderr.cpp",
+ "liblog_test_stderr_local.cpp",
+ "log_id_test.cpp",
+ "log_radio_test.cpp",
+ "log_read_test.cpp",
+ "log_system_test.cpp",
+ "log_time_test.cpp",
+ "log_wrap_test.cpp",
+ ],
+ shared_libs: [
+ "liblog",
+ "libcutils",
+ "libbase",
+ ],
+}
+
+// Build tests for the device (with .so). Run with:
+// adb shell /data/nativetest/liblog-unit-tests/liblog-unit-tests
+cc_test {
+ name: "liblog-unit-tests",
+ defaults: ["liblog-tests-defaults"],
+}
+
+cc_test {
+ name: "CtsLiblogTestCases",
+ defaults: ["liblog-tests-defaults"],
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
+
+ cflags: ["-DNO_PSTORE"],
+ test_suites: [
+ "cts",
+ "vts",
+ ],
+}
diff --git a/liblog/tests/Android.mk b/liblog/tests/Android.mk
deleted file mode 100644
index cfa849b..0000000
--- a/liblog/tests/Android.mk
+++ /dev/null
@@ -1,116 +0,0 @@
-#
-# Copyright (C) 2013-2014 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-# -----------------------------------------------------------------------------
-# Benchmarks.
-# -----------------------------------------------------------------------------
-
-test_module_prefix := liblog-
-test_tags := tests
-
-benchmark_c_flags := \
- -Wall \
- -Wextra \
- -Werror \
- -fno-builtin \
-
-benchmark_src_files := \
- liblog_benchmark.cpp
-
-# Build benchmarks for the device. Run with:
-# adb shell liblog-benchmarks
-include $(CLEAR_VARS)
-LOCAL_MODULE := $(test_module_prefix)benchmarks
-LOCAL_MODULE_TAGS := $(test_tags)
-LOCAL_CFLAGS += $(benchmark_c_flags)
-LOCAL_SHARED_LIBRARIES += liblog libm libbase
-LOCAL_SRC_FILES := $(benchmark_src_files)
-include $(BUILD_NATIVE_BENCHMARK)
-
-# -----------------------------------------------------------------------------
-# Unit tests.
-# -----------------------------------------------------------------------------
-
-test_c_flags := \
- -fstack-protector-all \
- -g \
- -Wall -Wextra \
- -Werror \
- -fno-builtin \
-
-cts_src_files := \
- libc_test.cpp \
- liblog_test_default.cpp \
- liblog_test_local.cpp \
- liblog_test_stderr.cpp \
- liblog_test_stderr_local.cpp \
- log_id_test.cpp \
- log_radio_test.cpp \
- log_read_test.cpp \
- log_system_test.cpp \
- log_time_test.cpp \
- log_wrap_test.cpp
-
-test_src_files := \
- $(cts_src_files) \
-
-# Build tests for the device (with .so). Run with:
-# adb shell /data/nativetest/liblog-unit-tests/liblog-unit-tests
-include $(CLEAR_VARS)
-LOCAL_MODULE := $(test_module_prefix)unit-tests
-LOCAL_MODULE_TAGS := $(test_tags)
-LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_SHARED_LIBRARIES := liblog libcutils libbase
-LOCAL_SRC_FILES := $(test_src_files)
-include $(BUILD_NATIVE_TEST)
-
-cts_executable := CtsLiblogTestCases
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := $(cts_executable)
-LOCAL_MODULE_TAGS := tests
-LOCAL_CFLAGS += $(test_c_flags) -DNO_PSTORE
-LOCAL_SRC_FILES := $(cts_src_files)
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/nativetest
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-LOCAL_SHARED_LIBRARIES := liblog libcutils libbase
-LOCAL_STATIC_LIBRARIES := libgtest libgtest_main
-LOCAL_COMPATIBILITY_SUITE := cts vts
-LOCAL_CTS_TEST_PACKAGE := android.core.liblog
-include $(BUILD_CTS_EXECUTABLE)
-
-ifeq ($(HOST_OS)-$(HOST_ARCH),$(filter $(HOST_OS)-$(HOST_ARCH),linux-x86 linux-x86_64))
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := $(cts_executable)_list
-LOCAL_MODULE_TAGS := optional
-LOCAL_CFLAGS := $(test_c_flags) -DHOST
-LOCAL_C_INCLUDES := external/gtest/include
-LOCAL_SRC_FILES := $(test_src_files)
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-LOCAL_CXX_STL := libc++
-LOCAL_SHARED_LIBRARIES := liblog libcutils libbase
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-LOCAL_LDLIBS_linux := -lrt
-include $(BUILD_HOST_NATIVE_TEST)
-
-endif # ifeq ($(HOST_OS)-$(HOST_ARCH),$(filter $(HOST_OS)-$(HOST_ARCH),linux-x86 linux-x86_64))
diff --git a/libmeminfo/.clang-format b/libmeminfo/.clang-format
new file mode 120000
index 0000000..1af4f51
--- /dev/null
+++ b/libmeminfo/.clang-format
@@ -0,0 +1 @@
+../.clang-format-4
\ No newline at end of file
diff --git a/libmeminfo/Android.bp b/libmeminfo/Android.bp
new file mode 100644
index 0000000..aab3743
--- /dev/null
+++ b/libmeminfo/Android.bp
@@ -0,0 +1,70 @@
+//
+// Copyright (C) 2018 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.
+//
+
+cc_defaults {
+ name: "libmeminfo_defaults",
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "liblog",
+ "libprocinfo",
+ ],
+}
+
+cc_library {
+ name: "libmeminfo",
+ defaults: ["libmeminfo_defaults"],
+ export_include_dirs: ["include"],
+ export_shared_lib_headers: ["libbase"],
+ srcs: [
+ "pageacct.cpp",
+ "procmeminfo.cpp",
+ "sysmeminfo.cpp",
+ ],
+}
+
+cc_test {
+ name: "libmeminfo_test",
+ defaults: ["libmeminfo_defaults"],
+
+ static_libs: [
+ "libmeminfo",
+ "libpagemap",
+ "libbase",
+ "liblog",
+ ],
+
+ srcs: [
+ "libmeminfo_test.cpp"
+ ],
+}
+
+cc_benchmark {
+ name: "libmeminfo_benchmark",
+ srcs: [
+ "libmeminfo_benchmark.cpp",
+ ],
+ static_libs : [
+ "libbase",
+ "liblog",
+ "libmeminfo",
+ "libprocinfo",
+ ],
+}
diff --git a/libmeminfo/include/meminfo/meminfo.h b/libmeminfo/include/meminfo/meminfo.h
new file mode 100644
index 0000000..c328648
--- /dev/null
+++ b/libmeminfo/include/meminfo/meminfo.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2018 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 <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+namespace android {
+namespace meminfo {
+
+struct MemUsage {
+ uint64_t vss;
+ uint64_t rss;
+ uint64_t pss;
+ uint64_t uss;
+
+ uint64_t private_clean;
+ uint64_t private_dirty;
+ uint64_t shared_clean;
+ uint64_t shared_dirty;
+
+ MemUsage()
+ : vss(0),
+ rss(0),
+ pss(0),
+ uss(0),
+ private_clean(0),
+ private_dirty(0),
+ shared_clean(0),
+ shared_dirty(0) {}
+
+ ~MemUsage() = default;
+
+ void clear() {
+ vss = rss = pss = uss = 0;
+ private_clean = private_dirty = shared_clean = shared_dirty = 0;
+ }
+};
+
+struct Vma {
+ uint64_t start;
+ uint64_t end;
+ uint64_t offset;
+ uint16_t flags;
+ std::string name;
+
+ Vma(uint64_t s, uint64_t e, uint64_t off, uint16_t f, const char* n)
+ : start(s), end(e), offset(off), flags(f), name(n) {}
+ ~Vma() = default;
+
+ // Memory usage of this mapping.
+ MemUsage usage;
+ // Working set within this mapping.
+ MemUsage wss;
+};
+
+} // namespace meminfo
+} // namespace android
diff --git a/libmeminfo/include/meminfo/pageacct.h b/libmeminfo/include/meminfo/pageacct.h
new file mode 100644
index 0000000..8ddaef2
--- /dev/null
+++ b/libmeminfo/include/meminfo/pageacct.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2018 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 <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace meminfo {
+
+class PageAcct final {
+ // Class for per-page accounting by using kernel provided interfaces like
+ // kpagecount, kpageflags etc.
+ public:
+ static bool KernelHasPageIdle() {
+ return (access("/sys/kernel/mm/page_idle/bitmap", R_OK | W_OK) == 0);
+ }
+
+ bool InitPageAcct(bool pageidle_enable = false);
+ bool PageFlags(uint64_t pfn, uint64_t* flags);
+ bool PageMapCount(uint64_t pfn, uint64_t* mapcount);
+
+ int IsPageIdle(uint64_t pfn);
+
+ // The only way to create PageAcct object
+ static PageAcct& Instance() {
+ static PageAcct instance;
+ return instance;
+ }
+
+ ~PageAcct() = default;
+
+ private:
+ PageAcct() : kpagecount_fd_(-1), kpageflags_fd_(-1), pageidle_fd_(-1) {}
+ int MarkPageIdle(uint64_t pfn) const;
+ int GetPageIdle(uint64_t pfn) const;
+
+ // Non-copyable & Non-movable
+ PageAcct(const PageAcct&) = delete;
+ PageAcct& operator=(const PageAcct&) = delete;
+ PageAcct& operator=(PageAcct&&) = delete;
+ PageAcct(PageAcct&&) = delete;
+
+ ::android::base::unique_fd kpagecount_fd_;
+ ::android::base::unique_fd kpageflags_fd_;
+ ::android::base::unique_fd pageidle_fd_;
+};
+
+} // namespace meminfo
+} // namespace android
diff --git a/libmeminfo/include/meminfo/procmeminfo.h b/libmeminfo/include/meminfo/procmeminfo.h
new file mode 100644
index 0000000..b37c56b
--- /dev/null
+++ b/libmeminfo/include/meminfo/procmeminfo.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 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 <sys/types.h>
+
+#include <string>
+#include <vector>
+
+#include "meminfo.h"
+
+namespace android {
+namespace meminfo {
+
+class ProcMemInfo final {
+ // Per-process memory accounting
+ public:
+ ProcMemInfo(pid_t pid, bool get_wss = false);
+
+ const std::vector<Vma>& Maps();
+ const MemUsage& Usage();
+ const MemUsage& Wss();
+
+ bool WssReset();
+ ~ProcMemInfo() = default;
+
+ private:
+ bool ReadMaps(bool get_wss);
+ bool ReadVmaStats(int pagemap_fd, Vma& vma, bool get_wss);
+
+ pid_t pid_;
+ bool get_wss_;
+
+ std::vector<Vma> maps_;
+
+ MemUsage usage_;
+ MemUsage wss_;
+};
+
+} // namespace meminfo
+} // namespace android
diff --git a/libmeminfo/include/meminfo/sysmeminfo.h b/libmeminfo/include/meminfo/sysmeminfo.h
new file mode 100644
index 0000000..f5e05bd
--- /dev/null
+++ b/libmeminfo/include/meminfo/sysmeminfo.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2018 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 <sys/types.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+namespace android {
+namespace meminfo {
+
+class SysMemInfo final {
+ // System or Global memory accounting
+ public:
+ static const std::vector<std::string> kDefaultSysMemInfoTags;
+
+ SysMemInfo() = default;
+
+ // Parse /proc/meminfo and read values that are needed
+ bool ReadMemInfo(const std::string& path = "/proc/meminfo");
+ bool ReadMemInfo(const std::vector<std::string>& tags,
+ const std::string& path = "/proc/meminfo");
+
+ // getters
+ uint64_t mem_total_kb() { return mem_in_kb_["MemTotal:"]; }
+ uint64_t mem_free_kb() { return mem_in_kb_["MemFree:"]; }
+ uint64_t mem_buffers_kb() { return mem_in_kb_["Buffers:"]; }
+ uint64_t mem_cached_kb() { return mem_in_kb_["Cached:"]; }
+ uint64_t mem_shmem_kb() { return mem_in_kb_["Shmem:"]; }
+ uint64_t mem_slab_kb() { return mem_in_kb_["Slab:"]; }
+ uint64_t mem_slab_reclailmable_kb() { return mem_in_kb_["SReclaimable:"]; }
+ uint64_t mem_slab_unreclaimable_kb() { return mem_in_kb_["SUnreclaim:"]; }
+ uint64_t mem_swap_kb() { return mem_in_kb_["SwapTotal:"]; }
+ uint64_t mem_free_swap_kb() { return mem_in_kb_["SwapFree:"]; }
+ uint64_t mem_zram_kb() { return mem_in_kb_["Zram:"]; }
+ uint64_t mem_mapped_kb() { return mem_in_kb_["Mapped:"]; }
+ uint64_t mem_vmalloc_used_kb() { return mem_in_kb_["VmallocUsed:"]; }
+ uint64_t mem_page_tables_kb() { return mem_in_kb_["PageTables:"]; }
+ uint64_t mem_kernel_stack_kb() { return mem_in_kb_["KernelStack:"]; }
+
+ private:
+ std::map<std::string, uint64_t> mem_in_kb_;
+};
+
+} // namespace meminfo
+} // namespace android
diff --git a/libmeminfo/libmeminfo_benchmark.cpp b/libmeminfo/libmeminfo_benchmark.cpp
new file mode 100644
index 0000000..e2239f0
--- /dev/null
+++ b/libmeminfo/libmeminfo_benchmark.cpp
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <meminfo/sysmeminfo.h>
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+
+#include <benchmark/benchmark.h>
+
+enum {
+ MEMINFO_TOTAL,
+ MEMINFO_FREE,
+ MEMINFO_BUFFERS,
+ MEMINFO_CACHED,
+ MEMINFO_SHMEM,
+ MEMINFO_SLAB,
+ MEMINFO_SLAB_RECLAIMABLE,
+ MEMINFO_SLAB_UNRECLAIMABLE,
+ MEMINFO_SWAP_TOTAL,
+ MEMINFO_SWAP_FREE,
+ MEMINFO_ZRAM_TOTAL,
+ MEMINFO_MAPPED,
+ MEMINFO_VMALLOC_USED,
+ MEMINFO_PAGE_TABLES,
+ MEMINFO_KERNEL_STACK,
+ MEMINFO_COUNT
+};
+
+void get_mem_info(uint64_t mem[], const char* file) {
+ char buffer[4096];
+ unsigned int numFound = 0;
+
+ int fd = open(file, O_RDONLY);
+
+ if (fd < 0) {
+ printf("Unable to open %s: %s\n", file, strerror(errno));
+ return;
+ }
+
+ const int len = read(fd, buffer, sizeof(buffer) - 1);
+ close(fd);
+
+ if (len < 0) {
+ printf("Empty %s\n", file);
+ return;
+ }
+ buffer[len] = 0;
+
+ static const char* const tags[] = {
+ "MemTotal:", "MemFree:", "Buffers:", "Cached:", "Shmem:", "Slab:",
+ "SReclaimable:", "SUnreclaim:", "SwapTotal:", "SwapFree:", "ZRam:", "Mapped:",
+ "VmallocUsed:", "PageTables:", "KernelStack:", NULL};
+
+ static const int tagsLen[] = {9, 8, 8, 7, 6, 5, 13, 11, 10, 9, 5, 7, 12, 11, 12, 0};
+
+ memset(mem, 0, sizeof(uint64_t) * 15);
+ char* p = buffer;
+ while (*p && (numFound < (sizeof(tagsLen) / sizeof(tagsLen[0])))) {
+ int i = 0;
+ while (tags[i]) {
+ //std::cout << "tag =" << tags[i] << " p = " << std::string(p, tagsLen[i]) << std::endl;
+ if (strncmp(p, tags[i], tagsLen[i]) == 0) {
+ p += tagsLen[i];
+ while (*p == ' ') p++;
+ char* num = p;
+ while (*p >= '0' && *p <= '9') p++;
+ if (*p != 0) {
+ *p = 0;
+ p++;
+ }
+ mem[i] = atoll(num);
+ numFound++;
+ break;
+ }
+ i++;
+ }
+ while (*p && *p != '\n') {
+ p++;
+ }
+ if (*p) p++;
+ }
+}
+
+static void BM_ParseSysMemInfo(benchmark::State& state) {
+ std::string meminfo = R"meminfo(MemTotal: 3019740 kB
+MemFree: 1809728 kB
+MemAvailable: 2546560 kB
+Buffers: 54736 kB
+Cached: 776052 kB
+SwapCached: 0 kB
+Active: 445856 kB
+Inactive: 459092 kB
+Active(anon): 78492 kB
+Inactive(anon): 2240 kB
+Active(file): 367364 kB
+Inactive(file): 456852 kB
+Unevictable: 3096 kB
+Mlocked: 3096 kB
+SwapTotal: 0 kB
+SwapFree: 0 kB
+Dirty: 32 kB
+Writeback: 0 kB
+AnonPages: 74988 kB
+Mapped: 62624 kB
+Shmem: 4020 kB
+Slab: 86464 kB
+SReclaimable: 44432 kB
+SUnreclaim: 42032 kB
+KernelStack: 4880 kB
+PageTables: 2900 kB
+NFS_Unstable: 0 kB
+Bounce: 0 kB
+WritebackTmp: 0 kB
+CommitLimit: 1509868 kB
+Committed_AS: 80296 kB
+VmallocTotal: 263061440 kB
+VmallocUsed: 0 kB
+VmallocChunk: 0 kB
+AnonHugePages: 6144 kB
+ShmemHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+CmaTotal: 131072 kB
+CmaFree: 130380 kB
+HugePages_Total: 0
+HugePages_Free: 0
+HugePages_Rsvd: 0
+HugePages_Surp: 0
+Hugepagesize: 2048 kB)meminfo";
+
+ TemporaryFile tf;
+ ::android::base::WriteStringToFd(meminfo, tf.fd);
+
+ uint64_t mem[MEMINFO_COUNT];
+ for (auto _ : state) {
+ get_mem_info(mem, tf.path);
+ }
+}
+BENCHMARK(BM_ParseSysMemInfo);
+
+static void BM_ReadMemInfo(benchmark::State& state) {
+ std::string meminfo = R"meminfo(MemTotal: 3019740 kB
+MemFree: 1809728 kB
+MemAvailable: 2546560 kB
+Buffers: 54736 kB
+Cached: 776052 kB
+SwapCached: 0 kB
+Active: 445856 kB
+Inactive: 459092 kB
+Active(anon): 78492 kB
+Inactive(anon): 2240 kB
+Active(file): 367364 kB
+Inactive(file): 456852 kB
+Unevictable: 3096 kB
+Mlocked: 3096 kB
+SwapTotal: 0 kB
+SwapFree: 0 kB
+Dirty: 32 kB
+Writeback: 0 kB
+AnonPages: 74988 kB
+Mapped: 62624 kB
+Shmem: 4020 kB
+Slab: 86464 kB
+SReclaimable: 44432 kB
+SUnreclaim: 42032 kB
+KernelStack: 4880 kB
+PageTables: 2900 kB
+NFS_Unstable: 0 kB
+Bounce: 0 kB
+WritebackTmp: 0 kB
+CommitLimit: 1509868 kB
+Committed_AS: 80296 kB
+VmallocTotal: 263061440 kB
+VmallocUsed: 0 kB
+VmallocChunk: 0 kB
+AnonHugePages: 6144 kB
+ShmemHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+CmaTotal: 131072 kB
+CmaFree: 130380 kB
+HugePages_Total: 0
+HugePages_Free: 0
+HugePages_Rsvd: 0
+HugePages_Surp: 0
+Hugepagesize: 2048 kB)meminfo";
+
+ TemporaryFile tf;
+ android::base::WriteStringToFd(meminfo, tf.fd);
+
+ std::string file = std::string(tf.path);
+ ::android::meminfo::SysMemInfo mi;
+ for (auto _ : state) {
+ mi.ReadMemInfo(file);
+ }
+}
+BENCHMARK(BM_ReadMemInfo);
+
+BENCHMARK_MAIN();
diff --git a/libmeminfo/libmeminfo_test.cpp b/libmeminfo/libmeminfo_test.cpp
new file mode 100644
index 0000000..7a2be41
--- /dev/null
+++ b/libmeminfo/libmeminfo_test.cpp
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+
+#include <string>
+#include <vector>
+
+#include <meminfo/pageacct.h>
+#include <meminfo/procmeminfo.h>
+#include <meminfo/sysmeminfo.h>
+#include <pagemap/pagemap.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+
+using namespace std;
+using namespace android::meminfo;
+
+pid_t pid = -1;
+
+class ValidateProcMemInfo : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ ASSERT_EQ(0, pm_kernel_create(&ker));
+ ASSERT_EQ(0, pm_process_create(ker, pid, &proc));
+ proc_mem = new ProcMemInfo(pid);
+ ASSERT_NE(proc_mem, nullptr);
+ }
+
+ void TearDown() override {
+ delete proc_mem;
+ pm_process_destroy(proc);
+ pm_kernel_destroy(ker);
+ }
+
+ pm_kernel_t* ker;
+ pm_process_t* proc;
+ ProcMemInfo* proc_mem;
+};
+
+TEST_F(ValidateProcMemInfo, TestMapsSize) {
+ const std::vector<Vma>& maps = proc_mem->Maps();
+ ASSERT_FALSE(maps.empty()) << "Process " << getpid() << " maps are empty";
+}
+
+TEST_F(ValidateProcMemInfo, TestMapsEquality) {
+ const std::vector<Vma>& maps = proc_mem->Maps();
+ ASSERT_EQ(proc->num_maps, maps.size());
+
+ for (size_t i = 0; i < maps.size(); ++i) {
+ EXPECT_EQ(proc->maps[i]->start, maps[i].start);
+ EXPECT_EQ(proc->maps[i]->end, maps[i].end);
+ EXPECT_EQ(proc->maps[i]->offset, maps[i].offset);
+ EXPECT_EQ(std::string(proc->maps[i]->name), maps[i].name);
+ }
+}
+
+TEST_F(ValidateProcMemInfo, TestMapsUsage) {
+ const std::vector<Vma>& maps = proc_mem->Maps();
+ ASSERT_FALSE(maps.empty());
+ ASSERT_EQ(proc->num_maps, maps.size());
+
+ pm_memusage_t map_usage, proc_usage;
+ pm_memusage_zero(&map_usage);
+ pm_memusage_zero(&proc_usage);
+ for (size_t i = 0; i < maps.size(); i++) {
+ ASSERT_EQ(0, pm_map_usage(proc->maps[i], &map_usage));
+ EXPECT_EQ(map_usage.vss, maps[i].usage.vss) << "VSS mismatch for map: " << maps[i].name;
+ EXPECT_EQ(map_usage.rss, maps[i].usage.rss) << "RSS mismatch for map: " << maps[i].name;
+ EXPECT_EQ(map_usage.pss, maps[i].usage.pss) << "PSS mismatch for map: " << maps[i].name;
+ EXPECT_EQ(map_usage.uss, maps[i].usage.uss) << "USS mismatch for map: " << maps[i].name;
+ pm_memusage_add(&proc_usage, &map_usage);
+ }
+
+ EXPECT_EQ(proc_usage.vss, proc_mem->Usage().vss);
+ EXPECT_EQ(proc_usage.rss, proc_mem->Usage().rss);
+ EXPECT_EQ(proc_usage.pss, proc_mem->Usage().pss);
+ EXPECT_EQ(proc_usage.uss, proc_mem->Usage().uss);
+}
+
+class ValidateProcMemInfoWss : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ ASSERT_EQ(0, pm_kernel_create(&ker));
+ ASSERT_EQ(0, pm_process_create(ker, pid, &proc));
+ proc_mem = new ProcMemInfo(pid, true);
+ ASSERT_NE(proc_mem, nullptr);
+ }
+
+ void TearDown() override {
+ delete proc_mem;
+ pm_process_destroy(proc);
+ pm_kernel_destroy(ker);
+ }
+
+ pm_kernel_t* ker;
+ pm_process_t* proc;
+ ProcMemInfo* proc_mem;
+};
+
+TEST_F(ValidateProcMemInfoWss, TestWorkingTestReset) {
+ // Expect reset to succeed
+ EXPECT_TRUE(proc_mem->WssReset());
+}
+
+TEST_F(ValidateProcMemInfoWss, TestWssEquality) {
+ // Read wss using libpagemap
+ pm_memusage_t wss_pagemap;
+ EXPECT_EQ(0, pm_process_workingset(proc, &wss_pagemap, 0));
+
+ // Read wss using libmeminfo
+ MemUsage wss = proc_mem->Wss();
+
+ // compare
+ EXPECT_EQ(wss_pagemap.rss, wss.rss);
+ EXPECT_EQ(wss_pagemap.pss, wss.pss);
+ EXPECT_EQ(wss_pagemap.uss, wss.uss);
+}
+
+class ValidatePageAcct : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ ASSERT_EQ(0, pm_kernel_create(&ker));
+ ASSERT_EQ(0, pm_process_create(ker, pid, &proc));
+ }
+
+ void TearDown() override {
+ pm_process_destroy(proc);
+ pm_kernel_destroy(ker);
+ }
+
+ pm_kernel_t* ker;
+ pm_process_t* proc;
+};
+
+TEST_F(ValidatePageAcct, TestPageFlags) {
+ PageAcct& pi = PageAcct::Instance();
+ pi.InitPageAcct(false);
+
+ uint64_t* pagemap;
+ size_t num_pages;
+ for (size_t i = 0; i < proc->num_maps; i++) {
+ ASSERT_EQ(0, pm_map_pagemap(proc->maps[i], &pagemap, &num_pages));
+ for (size_t j = 0; j < num_pages; j++) {
+ if (!PM_PAGEMAP_PRESENT(pagemap[j])) continue;
+
+ uint64_t pfn = PM_PAGEMAP_PFN(pagemap[j]);
+ uint64_t page_flags_pagemap, page_flags_meminfo;
+
+ ASSERT_EQ(0, pm_kernel_flags(ker, pfn, &page_flags_pagemap));
+ ASSERT_TRUE(pi.PageFlags(pfn, &page_flags_meminfo));
+ // check if page flags equal
+ EXPECT_EQ(page_flags_pagemap, page_flags_meminfo);
+ }
+ free(pagemap);
+ }
+}
+
+TEST_F(ValidatePageAcct, TestPageCounts) {
+ PageAcct& pi = PageAcct::Instance();
+ pi.InitPageAcct(false);
+
+ uint64_t* pagemap;
+ size_t num_pages;
+ for (size_t i = 0; i < proc->num_maps; i++) {
+ ASSERT_EQ(0, pm_map_pagemap(proc->maps[i], &pagemap, &num_pages));
+ for (size_t j = 0; j < num_pages; j++) {
+ uint64_t pfn = PM_PAGEMAP_PFN(pagemap[j]);
+ uint64_t map_count_pagemap, map_count_meminfo;
+
+ ASSERT_EQ(0, pm_kernel_count(ker, pfn, &map_count_pagemap));
+ ASSERT_TRUE(pi.PageMapCount(pfn, &map_count_meminfo));
+ // check if map counts are equal
+ EXPECT_EQ(map_count_pagemap, map_count_meminfo);
+ }
+ free(pagemap);
+ }
+}
+
+TEST_F(ValidatePageAcct, TestPageIdle) {
+ // skip the test if idle page tracking isn't enabled
+ if (pm_kernel_init_page_idle(ker) != 0) {
+ return;
+ }
+
+ PageAcct& pi = PageAcct::Instance();
+ ASSERT_TRUE(pi.InitPageAcct(true));
+
+ uint64_t* pagemap;
+ size_t num_pages;
+ for (size_t i = 0; i < proc->num_maps; i++) {
+ ASSERT_EQ(0, pm_map_pagemap(proc->maps[i], &pagemap, &num_pages));
+ for (size_t j = 0; j < num_pages; j++) {
+ if (!PM_PAGEMAP_PRESENT(pagemap[j])) continue;
+ uint64_t pfn = PM_PAGEMAP_PFN(pagemap[j]);
+
+ ASSERT_EQ(0, pm_kernel_mark_page_idle(ker, &pfn, 1));
+ int idle_status_pagemap = pm_kernel_get_page_idle(ker, pfn);
+ int idle_status_meminfo = pi.IsPageIdle(pfn);
+ EXPECT_EQ(idle_status_pagemap, idle_status_meminfo);
+ }
+ free(pagemap);
+ }
+}
+
+TEST(SysMemInfoParser, TestSysMemInfoFile) {
+ std::string meminfo = R"meminfo(MemTotal: 3019740 kB
+MemFree: 1809728 kB
+MemAvailable: 2546560 kB
+Buffers: 54736 kB
+Cached: 776052 kB
+SwapCached: 0 kB
+Active: 445856 kB
+Inactive: 459092 kB
+Active(anon): 78492 kB
+Inactive(anon): 2240 kB
+Active(file): 367364 kB
+Inactive(file): 456852 kB
+Unevictable: 3096 kB
+Mlocked: 3096 kB
+SwapTotal: 0 kB
+SwapFree: 0 kB
+Dirty: 32 kB
+Writeback: 0 kB
+AnonPages: 74988 kB
+Mapped: 62624 kB
+Shmem: 4020 kB
+Slab: 86464 kB
+SReclaimable: 44432 kB
+SUnreclaim: 42032 kB
+KernelStack: 4880 kB
+PageTables: 2900 kB
+NFS_Unstable: 0 kB
+Bounce: 0 kB
+WritebackTmp: 0 kB
+CommitLimit: 1509868 kB
+Committed_AS: 80296 kB
+VmallocTotal: 263061440 kB
+VmallocUsed: 0 kB
+VmallocChunk: 0 kB
+AnonHugePages: 6144 kB
+ShmemHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+CmaTotal: 131072 kB
+CmaFree: 130380 kB
+HugePages_Total: 0
+HugePages_Free: 0
+HugePages_Rsvd: 0
+HugePages_Surp: 0
+Hugepagesize: 2048 kB)meminfo";
+
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ ASSERT_TRUE(::android::base::WriteStringToFd(meminfo, tf.fd));
+
+ SysMemInfo mi;
+ ASSERT_TRUE(mi.ReadMemInfo(tf.path));
+ EXPECT_EQ(mi.mem_total_kb(), 3019740);
+ EXPECT_EQ(mi.mem_page_tables_kb(), 2900);
+}
+
+TEST(SysMemInfoParser, TestEmptyFile) {
+ TemporaryFile tf;
+ std::string empty_string = "";
+ ASSERT_TRUE(tf.fd != -1);
+ ASSERT_TRUE(::android::base::WriteStringToFd(empty_string, tf.fd));
+
+ SysMemInfo mi;
+ EXPECT_TRUE(mi.ReadMemInfo(tf.path));
+ EXPECT_EQ(mi.mem_total_kb(), 0);
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ if (argc <= 1) {
+ cerr << "Pid of a permanently sleeping process must be provided." << endl;
+ exit(EXIT_FAILURE);
+ }
+ ::android::base::InitLogging(argv, android::base::StderrLogger);
+ pid = std::stoi(std::string(argv[1]));
+ return RUN_ALL_TESTS();
+}
diff --git a/libmeminfo/meminfo_private.h b/libmeminfo/meminfo_private.h
new file mode 100644
index 0000000..df5699c
--- /dev/null
+++ b/libmeminfo/meminfo_private.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 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 <sys/types.h>
+#include <unistd.h>
+
+#include <meminfo/meminfo.h>
+#include <meminfo/pageacct.h>
+#include <meminfo/procmeminfo.h>
+#include <meminfo/sysmeminfo.h>
+
+// Macros to do per-page flag manipulation
+#define _BITS(x, offset, bits) (((x) >> (offset)) & ((1LL << (bits)) - 1))
+#define PAGE_PRESENT(x) (_BITS(x, 63, 1))
+#define PAGE_SWAPPED(x) (_BITS(x, 62, 1))
+#define PAGE_SHIFT(x) (_BITS(x, 55, 6))
+#define PAGE_PFN(x) (_BITS(x, 0, 55))
+#define PAGE_SWAP_OFFSET(x) (_BITS(x, 5, 50))
+#define PAGE_SWAP_TYPE(x) (_BITS(x, 0, 5))
diff --git a/libmeminfo/pageacct.cpp b/libmeminfo/pageacct.cpp
new file mode 100644
index 0000000..887a74d
--- /dev/null
+++ b/libmeminfo/pageacct.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+
+#include "meminfo_private.h"
+
+using unique_fd = ::android::base::unique_fd;
+
+namespace android {
+namespace meminfo {
+
+static inline off64_t pfn_to_idle_bitmap_offset(uint64_t pfn) {
+ return static_cast<off64_t>((pfn >> 6) << 3);
+}
+
+uint64_t pagesize(void) {
+ static uint64_t pagesize = sysconf(_SC_PAGE_SIZE);
+ return pagesize;
+}
+
+bool PageAcct::InitPageAcct(bool pageidle_enable) {
+ if (pageidle_enable && !PageAcct::KernelHasPageIdle()) {
+ LOG(ERROR) << "Idle page tracking is not supported by the kernel";
+ return false;
+ }
+
+ if (kpagecount_fd_ < 0) {
+ unique_fd count_fd(TEMP_FAILURE_RETRY(open("/proc/kpagecount", O_RDONLY | O_CLOEXEC)));
+ if (count_fd < 0) {
+ PLOG(ERROR) << "Failed to open /proc/kpagecount";
+ return false;
+ }
+ kpagecount_fd_ = std::move(count_fd);
+ }
+
+ if (kpageflags_fd_ < 0) {
+ unique_fd flags_fd(TEMP_FAILURE_RETRY(open("/proc/kpageflags", O_RDONLY | O_CLOEXEC)));
+ if (flags_fd < 0) {
+ PLOG(ERROR) << "Failed to open /proc/kpageflags";
+ return false;
+ }
+ kpageflags_fd_ = std::move(flags_fd);
+ }
+
+ if (pageidle_enable && pageidle_fd_ < 0) {
+ unique_fd idle_fd(
+ TEMP_FAILURE_RETRY(open("/sys/kernel/mm/page_idle/bitmap", O_RDWR | O_CLOEXEC)));
+ if (idle_fd < 0) {
+ PLOG(ERROR) << "Failed to open page idle bitmap";
+ return false;
+ }
+ pageidle_fd_ = std::move(idle_fd);
+ }
+
+ return true;
+}
+
+bool PageAcct::PageFlags(uint64_t pfn, uint64_t* flags) {
+ if (!flags) return false;
+
+ if (kpageflags_fd_ < 0) {
+ if (!InitPageAcct()) return false;
+ }
+
+ if (pread64(kpageflags_fd_, flags, sizeof(uint64_t), pfn * sizeof(uint64_t)) < 0) {
+ PLOG(ERROR) << "Failed to read page flags for page " << pfn;
+ return false;
+ }
+ return true;
+}
+
+bool PageAcct::PageMapCount(uint64_t pfn, uint64_t* mapcount) {
+ if (!mapcount) return false;
+
+ if (kpagecount_fd_ < 0) {
+ if (!InitPageAcct()) return false;
+ }
+
+ if (pread64(kpagecount_fd_, mapcount, sizeof(uint64_t), pfn * sizeof(uint64_t)) < 0) {
+ PLOG(ERROR) << "Failed to read map count for page " << pfn;
+ return false;
+ }
+ return true;
+}
+
+int PageAcct::IsPageIdle(uint64_t pfn) {
+ if (pageidle_fd_ < 0) {
+ if (!InitPageAcct(true)) return -EOPNOTSUPP;
+ }
+
+ int idle_status = MarkPageIdle(pfn);
+ if (idle_status) return idle_status;
+
+ return GetPageIdle(pfn);
+}
+
+int PageAcct::MarkPageIdle(uint64_t pfn) const {
+ off64_t offset = pfn_to_idle_bitmap_offset(pfn);
+ // set the bit corresponding to page frame
+ uint64_t idle_bits = 1ULL << (pfn % 64);
+
+ if (pwrite64(pageidle_fd_, &idle_bits, sizeof(uint64_t), offset) < 0) {
+ PLOG(ERROR) << "Failed to write page idle bitmap for page " << pfn;
+ return -errno;
+ }
+
+ return 0;
+}
+
+int PageAcct::GetPageIdle(uint64_t pfn) const {
+ off64_t offset = pfn_to_idle_bitmap_offset(pfn);
+ uint64_t idle_bits;
+
+ if (pread64(pageidle_fd_, &idle_bits, sizeof(uint64_t), offset) < 0) {
+ PLOG(ERROR) << "Failed to read page idle bitmap for page " << pfn;
+ return -errno;
+ }
+
+ return !!(idle_bits & (1ULL << (pfn % 64)));
+}
+
+} // namespace meminfo
+} // namespace android
diff --git a/libmeminfo/procmeminfo.cpp b/libmeminfo/procmeminfo.cpp
new file mode 100644
index 0000000..fe91d25
--- /dev/null
+++ b/libmeminfo/procmeminfo.cpp
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <linux/kernel-page-flags.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <fstream>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <procinfo/process_map.h>
+
+#include "meminfo_private.h"
+
+namespace android {
+namespace meminfo {
+
+static void add_mem_usage(MemUsage* to, const MemUsage& from) {
+ to->vss += from.vss;
+ to->rss += from.rss;
+ to->pss += from.pss;
+ to->uss += from.uss;
+
+ to->private_clean += from.private_clean;
+ to->private_dirty += from.private_dirty;
+
+ to->shared_clean += from.shared_clean;
+ to->shared_dirty += from.shared_dirty;
+}
+
+ProcMemInfo::ProcMemInfo(pid_t pid, bool get_wss) : pid_(pid), get_wss_(get_wss) {
+ if (!ReadMaps(get_wss_)) {
+ LOG(ERROR) << "Failed to read maps for Process " << pid_;
+ }
+}
+
+const std::vector<Vma>& ProcMemInfo::Maps() {
+ return maps_;
+}
+
+const MemUsage& ProcMemInfo::Usage() {
+ if (get_wss_) {
+ LOG(WARNING) << "Trying to read memory usage from working set object";
+ }
+ return usage_;
+}
+
+const MemUsage& ProcMemInfo::Wss() {
+ if (!get_wss_) {
+ LOG(WARNING) << "Trying to read working set when there is none";
+ }
+
+ return wss_;
+}
+
+bool ProcMemInfo::WssReset() {
+ if (!get_wss_) {
+ LOG(ERROR) << "Trying to reset working set from a memory usage counting object";
+ return false;
+ }
+
+ std::string clear_refs_path = ::android::base::StringPrintf("/proc/%d/clear_refs", pid_);
+ if (!::android::base::WriteStringToFile("1\n", clear_refs_path)) {
+ PLOG(ERROR) << "Failed to write to " << clear_refs_path;
+ return false;
+ }
+
+ wss_.clear();
+ return true;
+}
+
+bool ProcMemInfo::ReadMaps(bool get_wss) {
+ // parse and read /proc/<pid>/maps
+ std::string maps_file = ::android::base::StringPrintf("/proc/%d/maps", pid_);
+ if (!::android::procinfo::ReadMapFile(
+ maps_file, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff,
+ const char* name) {
+ maps_.emplace_back(Vma(start, end, pgoff, flags, name));
+ })) {
+ LOG(ERROR) << "Failed to parse " << maps_file;
+ maps_.clear();
+ return false;
+ }
+
+ std::string pagemap_file = ::android::base::StringPrintf("/proc/%d/pagemap", pid_);
+ ::android::base::unique_fd pagemap_fd(
+ TEMP_FAILURE_RETRY(open(pagemap_file.c_str(), O_RDONLY | O_CLOEXEC)));
+ if (pagemap_fd < 0) {
+ PLOG(ERROR) << "Failed to open " << pagemap_file;
+ return false;
+ }
+
+ for (auto& vma : maps_) {
+ if (!ReadVmaStats(pagemap_fd.get(), vma, get_wss)) {
+ LOG(ERROR) << "Failed to read page map for vma " << vma.name << "[" << vma.start
+ << "-" << vma.end << "]";
+ maps_.clear();
+ return false;
+ }
+ if (get_wss) {
+ add_mem_usage(&wss_, vma.wss);
+ } else {
+ add_mem_usage(&usage_, vma.usage);
+ }
+ }
+
+ return true;
+}
+
+bool ProcMemInfo::ReadVmaStats(int pagemap_fd, Vma& vma, bool get_wss) {
+ PageAcct& pinfo = PageAcct::Instance();
+ uint64_t pagesz = getpagesize();
+ uint64_t num_pages = (vma.end - vma.start) / pagesz;
+
+ std::unique_ptr<uint64_t[]> pg_frames(new uint64_t[num_pages]);
+ uint64_t first = vma.start / pagesz;
+ if (pread64(pagemap_fd, pg_frames.get(), num_pages * sizeof(uint64_t),
+ first * sizeof(uint64_t)) < 0) {
+ PLOG(ERROR) << "Failed to read page frames from page map for pid: " << pid_;
+ return false;
+ }
+
+ std::unique_ptr<uint64_t[]> pg_flags(new uint64_t[num_pages]);
+ std::unique_ptr<uint64_t[]> pg_counts(new uint64_t[num_pages]);
+ for (uint64_t i = 0; i < num_pages; ++i) {
+ if (!get_wss) {
+ vma.usage.vss += pagesz;
+ }
+ uint64_t p = pg_frames[i];
+ if (!PAGE_PRESENT(p) && !PAGE_SWAPPED(p)) continue;
+
+ if (PAGE_SWAPPED(p)) {
+ // TODO: do what's needed for swapped pages
+ continue;
+ }
+
+ uint64_t page_frame = PAGE_PFN(p);
+ if (!pinfo.PageFlags(page_frame, &pg_flags[i])) {
+ LOG(ERROR) << "Failed to get page flags for " << page_frame << " in process " << pid_;
+ return false;
+ }
+
+ if (!pinfo.PageMapCount(page_frame, &pg_counts[i])) {
+ LOG(ERROR) << "Failed to get page count for " << page_frame << " in process " << pid_;
+ return false;
+ }
+
+ // Page was unmapped between the presence check at the beginning of the loop and here.
+ if (pg_counts[i] == 0) {
+ pg_frames[i] = 0;
+ pg_flags[i] = 0;
+ continue;
+ }
+
+ bool is_dirty = !!(pg_flags[i] & (1 << KPF_DIRTY));
+ bool is_private = (pg_counts[i] == 1);
+ // Working set
+ if (get_wss) {
+ bool is_referenced = !!(pg_flags[i] & (1 << KPF_REFERENCED));
+ if (!is_referenced) {
+ continue;
+ }
+ // This effectively makes vss = rss for the working set is requested.
+ // The libpagemap implementation returns vss > rss for
+ // working set, which doesn't make sense.
+ vma.wss.vss += pagesz;
+ vma.wss.rss += pagesz;
+ vma.wss.uss += is_private ? pagesz : 0;
+ vma.wss.pss += pagesz / pg_counts[i];
+ if (is_private) {
+ vma.wss.private_dirty += is_dirty ? pagesz : 0;
+ vma.wss.private_clean += is_dirty ? 0 : pagesz;
+ } else {
+ vma.wss.shared_dirty += is_dirty ? pagesz : 0;
+ vma.wss.shared_clean += is_dirty ? 0 : pagesz;
+ }
+ } else {
+ vma.usage.rss += pagesz;
+ vma.usage.uss += is_private ? pagesz : 0;
+ vma.usage.pss += pagesz / pg_counts[i];
+ if (is_private) {
+ vma.usage.private_dirty += is_dirty ? pagesz : 0;
+ vma.usage.private_clean += is_dirty ? 0 : pagesz;
+ } else {
+ vma.usage.shared_dirty += is_dirty ? pagesz : 0;
+ vma.usage.shared_clean += is_dirty ? 0 : pagesz;
+ }
+ }
+ }
+
+ return true;
+}
+
+} // namespace meminfo
+} // namespace android
diff --git a/libmeminfo/sysmeminfo.cpp b/libmeminfo/sysmeminfo.cpp
new file mode 100644
index 0000000..50fa213
--- /dev/null
+++ b/libmeminfo/sysmeminfo.cpp
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <cctype>
+#include <fstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+
+#include "meminfo_private.h"
+
+namespace android {
+namespace meminfo {
+
+const std::vector<std::string> SysMemInfo::kDefaultSysMemInfoTags = {
+ "MemTotal:", "MemFree:", "Buffers:", "Cached:", "Shmem:",
+ "Slab:", "SReclaimable:", "SUnreclaim:", "SwapTotal:", "SwapFree:",
+ "ZRam:", "Mapped:", "VmallocUsed:", "PageTables:", "KernelStack:",
+};
+
+bool SysMemInfo::ReadMemInfo(const std::string& path) {
+ return ReadMemInfo(SysMemInfo::kDefaultSysMemInfoTags, path);
+}
+
+// TODO: Delete this function if it can't match up with the c-like implementation below.
+// Currently, this added about 50 % extra overhead on hikey.
+#if 0
+bool SysMemInfo::ReadMemInfo(const std::vector<std::string>& tags, const std::string& path) {
+ std::string buffer;
+ if (!::android::base::ReadFileToString(path, &buffer)) {
+ PLOG(ERROR) << "Failed to read : " << path;
+ return false;
+ }
+
+ uint32_t total_found = 0;
+ for (auto s = buffer.begin(); s < buffer.end() && total_found < tags.size();) {
+ for (auto& tag : tags) {
+ if (tag == std::string(s, s + tag.size())) {
+ s += tag.size();
+ while (isspace(*s)) s++;
+ auto num_start = s;
+ while (std::isdigit(*s)) s++;
+
+ std::string number(num_start, num_start + (s - num_start));
+ if (!::android::base::ParseUint(number, &mem_in_kb_[tag])) {
+ LOG(ERROR) << "Failed to parse uint";
+ return false;
+ }
+ total_found++;
+ break;
+ }
+ }
+ while (s < buffer.end() && *s != '\n') s++;
+ if (s < buffer.end()) s++;
+ }
+
+ return true;
+}
+
+#else
+bool SysMemInfo::ReadMemInfo(const std::vector<std::string>& tags, const std::string& path) {
+ char buffer[4096];
+ int fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
+ if (fd < 0) {
+ PLOG(ERROR) << "Failed to open file :" << path;
+ return false;
+ }
+
+ const int len = read(fd, buffer, sizeof(buffer) - 1);
+ close(fd);
+ if (len < 0) {
+ return false;
+ }
+
+ buffer[len] = '\0';
+ char* p = buffer;
+ uint32_t found = 0;
+ while (*p && found < tags.size()) {
+ for (auto& tag : tags) {
+ if (strncmp(p, tag.c_str(), tag.size()) == 0) {
+ p += tag.size();
+ while (*p == ' ') p++;
+ char* endptr = nullptr;
+ mem_in_kb_[tag] = strtoull(p, &endptr, 10);
+ if (p == endptr) {
+ PLOG(ERROR) << "Failed to parse line in file: " << path;
+ return false;
+ }
+ p = endptr;
+ found++;
+ break;
+ }
+ }
+ while (*p && *p != '\n') {
+ p++;
+ }
+ if (*p) p++;
+ }
+
+ return true;
+}
+#endif
+
+} // namespace meminfo
+} // namespace android
diff --git a/libmeminfo/tools/Android.bp b/libmeminfo/tools/Android.bp
new file mode 100644
index 0000000..0870130
--- /dev/null
+++ b/libmeminfo/tools/Android.bp
@@ -0,0 +1,27 @@
+// Copyright (C) 2018 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.
+
+cc_binary {
+ name: "procmem2",
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-unused-parameter",
+ ],
+
+ srcs: ["procmem.cpp"],
+ shared_libs: [
+ "libmeminfo",
+ ],
+}
diff --git a/libmeminfo/tools/procmem.cpp b/libmeminfo/tools/procmem.cpp
new file mode 100644
index 0000000..3571e41
--- /dev/null
+++ b/libmeminfo/tools/procmem.cpp
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <meminfo/procmeminfo.h>
+
+using ProcMemInfo = ::android::meminfo::ProcMemInfo;
+using MemUsage = ::android::meminfo::MemUsage;
+
+static void usage(const char* cmd) {
+ fprintf(stderr,
+ "Usage: %s [-i] [ -w | -W ] [ -p | -m ] [ -h ] pid\n"
+ " -i Uses idle page tracking for working set statistics.\n"
+ " -w Displays statistics for the working set only.\n"
+ " -W Resets the working set of the process.\n"
+ " -p Sort by PSS.\n"
+ " -u Sort by USS.\n"
+ " -m Sort by mapping order (as read from /proc).\n"
+ " -h Hide maps with no RSS.\n",
+ cmd);
+}
+
+static void show_footer(uint32_t nelem, const std::string& padding) {
+ std::string elem(7, '-');
+
+ for (uint32_t i = 0; i < nelem; ++i) {
+ std::cout << std::setw(7) << elem << padding;
+ }
+ std::cout << std::endl;
+}
+
+static void show_header(const std::vector<std::string>& header, const std::string& padding) {
+ if (header.empty()) return;
+
+ for (size_t i = 0; i < header.size() - 1; ++i) {
+ std::cout << std::setw(7) << header[i] << padding;
+ }
+ std::cout << header.back() << std::endl;
+ show_footer(header.size() - 1, padding);
+}
+
+static void scan_usage(std::stringstream& ss, const MemUsage& usage, const std::string& padding,
+ bool show_wss) {
+ // clear string stream first.
+ ss.str("");
+ // TODO: use ::android::base::StringPrintf instead of <iomanip> here.
+ if (!show_wss)
+ ss << std::setw(6) << usage.vss/1024 << padding;
+ ss << std::setw(6) << usage.rss/1024 << padding << std::setw(6)
+ << usage.pss/1024 << padding << std::setw(6) << usage.uss/1024 << padding
+ << std::setw(6) << usage.shared_clean/1024 << padding << std::setw(6)
+ << usage.shared_dirty/1024 << padding << std::setw(6)
+ << usage.private_clean/1024 << padding << std::setw(6)
+ << usage.private_dirty/1024 << padding;
+}
+
+static int show(ProcMemInfo& proc, bool hide_zeroes, bool show_wss) {
+ const std::vector<std::string> main_header = {"Vss", "Rss", "Pss", "Uss", "ShCl",
+ "ShDi", "PrCl", "PrDi", "Name"};
+ const std::vector<std::string> wss_header = {"WRss", "WPss", "WUss", "WShCl",
+ "WShDi", "WPrCl", "WPrDi", "Name"};
+ const std::vector<std::string>& header = show_wss ? wss_header : main_header;
+
+ // Read process memory stats
+ const MemUsage& stats = show_wss ? proc.Wss() : proc.Usage();
+ const std::vector<::android::meminfo::Vma>& maps = proc.Maps();
+
+ // following retains 'procmem' output so as to not break any scripts
+ // that rely on it.
+ std::string spaces = " ";
+ show_header(header, spaces);
+ const std::string padding = "K ";
+ std::stringstream ss;
+ for (auto& vma : maps) {
+ const MemUsage& vma_stats = show_wss ? vma.wss : vma.usage;
+ if (hide_zeroes && vma_stats.rss == 0) {
+ continue;
+ }
+ scan_usage(ss, vma_stats, padding, show_wss);
+ ss << vma.name << std::endl;
+ std::cout << ss.str();
+ }
+ show_footer(header.size() - 1, spaces);
+ scan_usage(ss, stats, padding, show_wss);
+ ss << "TOTAL" << std::endl;
+ std::cout << ss.str();
+
+ return 0;
+}
+
+int main(int argc, char* argv[]) {
+ bool use_pageidle = false;
+ bool hide_zeroes = false;
+ bool wss_reset = false;
+ bool show_wss = false;
+ int opt;
+
+ while ((opt = getopt(argc, argv, "himpuWw")) != -1) {
+ switch (opt) {
+ case 'h':
+ hide_zeroes = true;
+ break;
+ case 'i':
+ use_pageidle = true;
+ break;
+ case 'm':
+ break;
+ case 'p':
+ break;
+ case 'u':
+ break;
+ case 'W':
+ wss_reset = true;
+ break;
+ case 'w':
+ show_wss = true;
+ break;
+ case '?':
+ usage(argv[0]);
+ return 0;
+ default:
+ abort();
+ }
+ }
+
+ if (optind != (argc - 1)) {
+ fprintf(stderr, "Need exactly one pid at the end\n");
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ pid_t pid = atoi(argv[optind]);
+ if (pid == 0) {
+ std::cerr << "Invalid process id" << std::endl;
+ exit(EXIT_FAILURE);
+ }
+
+ bool need_wss = wss_reset || show_wss;
+ ProcMemInfo proc(pid, need_wss);
+ if (wss_reset) {
+ if (!proc.WssReset()) {
+ std::cerr << "Failed to reset working set of pid : " << pid << std::endl;
+ exit(EXIT_FAILURE);
+ }
+ return 0;
+ }
+
+ return show(proc, hide_zeroes, show_wss);
+}
diff --git a/libmemunreachable/Android.bp b/libmemunreachable/Android.bp
index 248a9d2..b78a4c4 100644
--- a/libmemunreachable/Android.bp
+++ b/libmemunreachable/Android.bp
@@ -46,12 +46,6 @@
static_libs: ["libunwind_llvm"],
},
},
-
- // TODO(b/78118944), clang lld link flags do not work with special link
- // rules for libunwind_llvm yet. Linked aosp_arm-eng image failed to
- // boot up in the emulator.
- use_clang_lld: false,
-
export_include_dirs: ["include"],
local_include_dirs: ["include"],
}
diff --git a/libmetricslogger/Android.bp b/libmetricslogger/Android.bp
index 2d327ee..7d7554b 100644
--- a/libmetricslogger/Android.bp
+++ b/libmetricslogger/Android.bp
@@ -34,15 +34,6 @@
export_shared_lib_headers: ["libstatssocket"],
}
-// static version of libmetricslogger, needed by a few art static binaries
-// TODO(b/117829226): Remove once dependencies are cleaned up.
-cc_library_static {
- name: "libmetricslogger_static",
- srcs: metricslogger_lib_src_files,
- defaults: ["metricslogger_defaults"],
- export_shared_lib_headers: ["libstatssocket"],
-}
-
// metricslogger shared library, debug
// -----------------------------------------------------------------------------
cc_library_shared {
diff --git a/libnativebridge/Android.mk b/libnativebridge/Android.mk
deleted file mode 100644
index 3887b1b..0000000
--- a/libnativebridge/Android.mk
+++ /dev/null
@@ -1,3 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(LOCAL_PATH)/tests/Android.mk
diff --git a/libnativebridge/tests/Android.bp b/libnativebridge/tests/Android.bp
index 9e2e641..222bc4c 100644
--- a/libnativebridge/tests/Android.bp
+++ b/libnativebridge/tests/Android.bp
@@ -44,3 +44,45 @@
srcs: ["DummyNativeBridge3.cpp"],
defaults: ["libnativebridge-dummy-defaults"],
}
+
+// Build the unit tests.
+cc_test {
+ name: "libnativebridge-tests",
+ host_supported: true,
+ test_per_src: true,
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ srcs: [
+ "CodeCacheCreate_test.cpp",
+ "CodeCacheExists_test.cpp",
+ "CodeCacheStatFail_test.cpp",
+ "CompleteFlow_test.cpp",
+ "InvalidCharsNativeBridge_test.cpp",
+ "NativeBridge2Signal_test.cpp",
+ "NativeBridgeVersion_test.cpp",
+ "NeedsNativeBridge_test.cpp",
+ "PreInitializeNativeBridge_test.cpp",
+ "PreInitializeNativeBridgeFail1_test.cpp",
+ "PreInitializeNativeBridgeFail2_test.cpp",
+ "ReSetupNativeBridge_test.cpp",
+ "UnavailableNativeBridge_test.cpp",
+ "ValidNameNativeBridge_test.cpp",
+ "NativeBridge3UnloadLibrary_test.cpp",
+ "NativeBridge3GetError_test.cpp",
+ "NativeBridge3IsPathSupported_test.cpp",
+ "NativeBridge3InitAnonymousNamespace_test.cpp",
+ "NativeBridge3CreateNamespace_test.cpp",
+ "NativeBridge3LoadLibraryExt_test.cpp",
+ ],
+
+ shared_libs: [
+ "liblog",
+ "libnativebridge",
+ "libnativebridge-dummy",
+ ],
+ header_libs: ["libbase_headers"],
+}
diff --git a/libnativebridge/tests/Android.mk b/libnativebridge/tests/Android.mk
deleted file mode 100644
index 4ed6e20..0000000
--- a/libnativebridge/tests/Android.mk
+++ /dev/null
@@ -1,60 +0,0 @@
-# Build the unit tests.
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-# Build the unit tests.
-test_src_files := \
- CodeCacheCreate_test.cpp \
- CodeCacheExists_test.cpp \
- CodeCacheStatFail_test.cpp \
- CompleteFlow_test.cpp \
- InvalidCharsNativeBridge_test.cpp \
- NativeBridge2Signal_test.cpp \
- NativeBridgeVersion_test.cpp \
- NeedsNativeBridge_test.cpp \
- PreInitializeNativeBridge_test.cpp \
- PreInitializeNativeBridgeFail1_test.cpp \
- PreInitializeNativeBridgeFail2_test.cpp \
- ReSetupNativeBridge_test.cpp \
- UnavailableNativeBridge_test.cpp \
- ValidNameNativeBridge_test.cpp \
- NativeBridge3UnloadLibrary_test.cpp \
- NativeBridge3GetError_test.cpp \
- NativeBridge3IsPathSupported_test.cpp \
- NativeBridge3InitAnonymousNamespace_test.cpp \
- NativeBridge3CreateNamespace_test.cpp \
- NativeBridge3LoadLibraryExt_test.cpp
-
-
-shared_libraries := \
- liblog \
- libnativebridge \
- libnativebridge-dummy
-
-header_libraries := \
- libbase_headers
-
-libnativebridge_tests_common_cflags := \
- -Wall \
- -Werror \
-
-$(foreach file,$(test_src_files), \
- $(eval include $(CLEAR_VARS)) \
- $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
- $(eval LOCAL_HEADER_LIBRARIES := $(header_libraries)) \
- $(eval LOCAL_SRC_FILES := $(file)) \
- $(eval LOCAL_CFLAGS := $(libnativebridge_tests_common_cflags)) \
- $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
- $(eval include $(BUILD_NATIVE_TEST)) \
-)
-
-$(foreach file,$(test_src_files), \
- $(eval include $(CLEAR_VARS)) \
- $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
- $(eval LOCAL_HEADER_LIBRARIES := $(header_libraries)) \
- $(eval LOCAL_SRC_FILES := $(file)) \
- $(eval LOCAL_CFLAGS := $(libnativebridge_tests_common_cflags)) \
- $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
- $(eval include $(BUILD_HOST_NATIVE_TEST)) \
-)
diff --git a/libpixelflinger/Android.bp b/libpixelflinger/Android.bp
new file mode 100644
index 0000000..76d9444
--- /dev/null
+++ b/libpixelflinger/Android.bp
@@ -0,0 +1,115 @@
+cc_defaults {
+ name: "pixelflinger_defaults",
+
+ cflags: [
+ "-fstrict-aliasing",
+ "-fomit-frame-pointer",
+ "-Wall",
+ "-Werror",
+ "-Wno-unused-function",
+ ],
+ export_include_dirs: ["include"],
+ header_libs: ["libbase_headers"],
+ shared_libs: [
+ "libcutils",
+ "liblog",
+ "libutils",
+ ],
+
+ arch: {
+ arm: {
+ neon: {
+ cflags: ["-D__ARM_HAVE_NEON"],
+ },
+ },
+ },
+}
+
+cc_library_static {
+ name: "libpixelflinger-arm",
+ defaults: ["pixelflinger_defaults"],
+
+ srcs: [
+ "fixed.cpp",
+ "picker.cpp",
+ "pixelflinger.cpp",
+ "trap.cpp",
+ "scanline.cpp",
+ ],
+
+ arch: {
+ arm: {
+ instruction_set: "arm",
+ },
+ },
+}
+
+// For the tests to use
+cc_library_headers {
+ name: "libpixelflinger_internal",
+ export_include_dirs: [
+ "include",
+ ".",
+ ],
+}
+
+cc_library {
+ name: "libpixelflinger",
+ defaults: ["pixelflinger_defaults"],
+
+ srcs: [
+ "codeflinger/ARMAssemblerInterface.cpp",
+ "codeflinger/ARMAssemblerProxy.cpp",
+ "codeflinger/CodeCache.cpp",
+ "codeflinger/GGLAssembler.cpp",
+ "codeflinger/load_store.cpp",
+ "codeflinger/blending.cpp",
+ "codeflinger/texturing.cpp",
+ "format.cpp",
+ "clear.cpp",
+ "raster.cpp",
+ "buffer.cpp",
+ ],
+ whole_static_libs: ["libpixelflinger-arm"],
+
+ arch: {
+ arm: {
+ srcs: [
+ "codeflinger/ARMAssembler.cpp",
+ "codeflinger/disassem.c",
+ "col32cb16blend.S",
+ "t32cb16blend.S",
+ ],
+
+ neon: {
+ srcs: ["col32cb16blend_neon.S"],
+ },
+ },
+ arm64: {
+ srcs: [
+ "codeflinger/Arm64Assembler.cpp",
+ "codeflinger/Arm64Disassembler.cpp",
+ "arch-arm64/col32cb16blend.S",
+ "arch-arm64/t32cb16blend.S",
+ ],
+ },
+ mips: {
+ mips32r6: {
+ srcs: [
+ "codeflinger/MIPSAssembler.cpp",
+ "codeflinger/mips_disassem.c",
+ "arch-mips/t32cb16blend.S",
+ ],
+ },
+ },
+ mips64: {
+ srcs: [
+ "codeflinger/MIPSAssembler.cpp",
+ "codeflinger/MIPS64Assembler.cpp",
+ "codeflinger/mips64_disassem.c",
+ "arch-mips64/col32cb16blend.S",
+ "arch-mips64/t32cb16blend.S",
+ ],
+ },
+ },
+}
diff --git a/libpixelflinger/Android.mk b/libpixelflinger/Android.mk
deleted file mode 100644
index 8c80f6a..0000000
--- a/libpixelflinger/Android.mk
+++ /dev/null
@@ -1,81 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-#
-# C/C++ and ARMv5 objects
-#
-
-include $(CLEAR_VARS)
-PIXELFLINGER_SRC_FILES:= \
- codeflinger/ARMAssemblerInterface.cpp \
- codeflinger/ARMAssemblerProxy.cpp \
- codeflinger/CodeCache.cpp \
- codeflinger/GGLAssembler.cpp \
- codeflinger/load_store.cpp \
- codeflinger/blending.cpp \
- codeflinger/texturing.cpp \
- fixed.cpp.arm \
- picker.cpp.arm \
- pixelflinger.cpp.arm \
- trap.cpp.arm \
- scanline.cpp.arm \
- format.cpp \
- clear.cpp \
- raster.cpp \
- buffer.cpp
-
-PIXELFLINGER_CFLAGS := -fstrict-aliasing -fomit-frame-pointer
-PIXELFLINGER_CFLAGS += -Wall -Werror
-PIXELFLINGER_CFLAGS += -Wno-unused-function
-
-PIXELFLINGER_SRC_FILES_arm := \
- codeflinger/ARMAssembler.cpp \
- codeflinger/disassem.c \
- col32cb16blend.S \
- t32cb16blend.S \
-
-ifeq ($(ARCH_ARM_HAVE_NEON),true)
-PIXELFLINGER_SRC_FILES_arm += col32cb16blend_neon.S
-PIXELFLINGER_CFLAGS_arm += -D__ARM_HAVE_NEON
-endif
-
-PIXELFLINGER_SRC_FILES_arm64 := \
- codeflinger/Arm64Assembler.cpp \
- codeflinger/Arm64Disassembler.cpp \
- arch-arm64/col32cb16blend.S \
- arch-arm64/t32cb16blend.S \
-
-ifndef ARCH_MIPS_REV6
-PIXELFLINGER_SRC_FILES_mips := \
- codeflinger/MIPSAssembler.cpp \
- codeflinger/mips_disassem.c \
- arch-mips/t32cb16blend.S \
-
-endif
-
-PIXELFLINGER_SRC_FILES_mips64 := \
- codeflinger/MIPSAssembler.cpp \
- codeflinger/MIPS64Assembler.cpp \
- codeflinger/mips64_disassem.c \
- arch-mips64/col32cb16blend.S \
- arch-mips64/t32cb16blend.S \
-
-#
-# Shared library
-#
-
-LOCAL_MODULE:= libpixelflinger
-LOCAL_SRC_FILES := $(PIXELFLINGER_SRC_FILES)
-LOCAL_SRC_FILES_arm := $(PIXELFLINGER_SRC_FILES_arm)
-LOCAL_SRC_FILES_arm64 := $(PIXELFLINGER_SRC_FILES_arm64)
-LOCAL_SRC_FILES_mips := $(PIXELFLINGER_SRC_FILES_mips)
-LOCAL_SRC_FILES_mips64 := $(PIXELFLINGER_SRC_FILES_mips64)
-LOCAL_CFLAGS := $(PIXELFLINGER_CFLAGS)
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_C_INCLUDES += $(LOCAL_EXPORT_C_INCLUDE_DIRS)
-LOCAL_HEADER_LIBRARIES := libbase_headers
-LOCAL_SHARED_LIBRARIES := libcutils liblog libutils
-
-include $(BUILD_SHARED_LIBRARY)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/libpixelflinger/tests/Android.bp b/libpixelflinger/tests/Android.bp
new file mode 100644
index 0000000..820a84d
--- /dev/null
+++ b/libpixelflinger/tests/Android.bp
@@ -0,0 +1,16 @@
+cc_defaults {
+ name: "pixelflinger-tests",
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ header_libs: ["libpixelflinger_internal"],
+ static_libs: [
+ "libcutils",
+ "liblog",
+ "libpixelflinger",
+ "libutils",
+ ],
+}
diff --git a/libpixelflinger/tests/Android.mk b/libpixelflinger/tests/Android.mk
deleted file mode 100644
index 6571161..0000000
--- a/libpixelflinger/tests/Android.mk
+++ /dev/null
@@ -1 +0,0 @@
-include $(all-subdir-makefiles)
diff --git a/libpixelflinger/tests/arch-arm64/Android.bp b/libpixelflinger/tests/arch-arm64/Android.bp
new file mode 100644
index 0000000..2f5586a
--- /dev/null
+++ b/libpixelflinger/tests/arch-arm64/Android.bp
@@ -0,0 +1,11 @@
+cc_defaults {
+ name: "pixelflinger-tests-arm64",
+ defaults: ["pixelflinger-tests"],
+
+ enabled: false,
+ arch: {
+ arm64: {
+ enabled: true,
+ },
+ },
+}
diff --git a/libpixelflinger/tests/arch-arm64/Android.mk b/libpixelflinger/tests/arch-arm64/Android.mk
deleted file mode 100644
index ca58b4b..0000000
--- a/libpixelflinger/tests/arch-arm64/Android.mk
+++ /dev/null
@@ -1,3 +0,0 @@
-ifeq ($(TARGET_ARCH),arm64)
-include $(all-subdir-makefiles)
-endif
diff --git a/libpixelflinger/tests/arch-arm64/assembler/Android.bp b/libpixelflinger/tests/arch-arm64/assembler/Android.bp
new file mode 100644
index 0000000..003f485
--- /dev/null
+++ b/libpixelflinger/tests/arch-arm64/assembler/Android.bp
@@ -0,0 +1,9 @@
+cc_test {
+ name: "test-pixelflinger-arm64-assembler-test",
+ defaults: ["pixelflinger-tests-arm64"],
+
+ srcs: [
+ "arm64_assembler_test.cpp",
+ "asm_test_jacket.S",
+ ],
+}
diff --git a/libpixelflinger/tests/arch-arm64/assembler/Android.mk b/libpixelflinger/tests/arch-arm64/assembler/Android.mk
deleted file mode 100644
index db5dc4d..0000000
--- a/libpixelflinger/tests/arch-arm64/assembler/Android.mk
+++ /dev/null
@@ -1,23 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- arm64_assembler_test.cpp\
- asm_test_jacket.S
-
-LOCAL_SHARED_LIBRARIES := \
- libcutils \
- libpixelflinger
-
-LOCAL_C_INCLUDES := \
- $(LOCAL_PATH)/../../..
-
-LOCAL_MODULE:= test-pixelflinger-arm64-assembler-test
-
-LOCAL_CFLAGS := -Wall -Werror
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_MULTILIB := 64
-
-include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.bp b/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.bp
new file mode 100644
index 0000000..e640aeb
--- /dev/null
+++ b/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.bp
@@ -0,0 +1,6 @@
+cc_test {
+ name: "test-pixelflinger-arm64-col32cb16blend",
+ defaults: ["pixelflinger-tests-arm64"],
+
+ srcs: ["col32cb16blend_test.c"],
+}
diff --git a/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.mk b/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.mk
deleted file mode 100644
index 3096232..0000000
--- a/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.mk
+++ /dev/null
@@ -1,20 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- col32cb16blend_test.c \
- ../../../arch-arm64/col32cb16blend.S
-
-LOCAL_SHARED_LIBRARIES :=
-
-LOCAL_C_INCLUDES :=
-
-LOCAL_MODULE:= test-pixelflinger-arm64-col32cb16blend
-
-LOCAL_CFLAGS := -Wall -Werror
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_MULTILIB := 64
-
-include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-arm64/disassembler/Android.bp b/libpixelflinger/tests/arch-arm64/disassembler/Android.bp
new file mode 100644
index 0000000..38dc99a
--- /dev/null
+++ b/libpixelflinger/tests/arch-arm64/disassembler/Android.bp
@@ -0,0 +1,6 @@
+cc_test {
+ name: "test-pixelflinger-arm64-disassembler-test",
+ defaults: ["pixelflinger-tests-arm64"],
+
+ srcs: ["arm64_diassembler_test.cpp"],
+}
diff --git a/libpixelflinger/tests/arch-arm64/disassembler/Android.mk b/libpixelflinger/tests/arch-arm64/disassembler/Android.mk
deleted file mode 100644
index 78f12af..0000000
--- a/libpixelflinger/tests/arch-arm64/disassembler/Android.mk
+++ /dev/null
@@ -1,18 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- arm64_diassembler_test.cpp \
- ../../../codeflinger/Arm64Disassembler.cpp
-
-LOCAL_SHARED_LIBRARIES :=
-
-LOCAL_MODULE:= test-pixelflinger-arm64-disassembler-test
-
-LOCAL_CFLAGS := -Wall -Werror
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_MULTILIB := 64
-
-include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.bp b/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.bp
new file mode 100644
index 0000000..9d060d1
--- /dev/null
+++ b/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.bp
@@ -0,0 +1,6 @@
+cc_test {
+ name: "test-pixelflinger-arm64-t32cb16blend",
+ defaults: ["pixelflinger-tests-arm64"],
+
+ srcs: ["t32cb16blend_test.c"],
+}
diff --git a/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.mk b/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.mk
deleted file mode 100644
index 664347f..0000000
--- a/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.mk
+++ /dev/null
@@ -1,20 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- t32cb16blend_test.c \
- ../../../arch-arm64/t32cb16blend.S
-
-LOCAL_SHARED_LIBRARIES :=
-
-LOCAL_C_INCLUDES :=
-
-LOCAL_MODULE:= test-pixelflinger-arm64-t32cb16blend
-
-LOCAL_CFLAGS := -Wall -Werror
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_MULTILIB := 64
-
-include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-mips/Android.bp b/libpixelflinger/tests/arch-mips/Android.bp
new file mode 100644
index 0000000..2ca2721
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips/Android.bp
@@ -0,0 +1,11 @@
+cc_defaults {
+ name: "pixelflinger-tests-mips",
+ defaults: ["pixelflinger-tests"],
+
+ enabled: false,
+ arch: {
+ mips: {
+ enabled: true,
+ },
+ },
+}
diff --git a/libpixelflinger/tests/arch-mips/Android.mk b/libpixelflinger/tests/arch-mips/Android.mk
deleted file mode 100644
index fe6979e..0000000
--- a/libpixelflinger/tests/arch-mips/Android.mk
+++ /dev/null
@@ -1,6 +0,0 @@
-ifeq ($(TARGET_ARCH),mips)
-include $(all-subdir-makefiles)
-endif
-ifeq ($(TARGET_ARCH),mipsel)
-include $(all-subdir-makefiles)
-endif
diff --git a/libpixelflinger/tests/arch-mips/col32cb16blend/Android.bp b/libpixelflinger/tests/arch-mips/col32cb16blend/Android.bp
new file mode 100644
index 0000000..45bfe29
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips/col32cb16blend/Android.bp
@@ -0,0 +1,6 @@
+cc_test {
+ name: "test-pixelflinger-mips-col32cb16blend",
+ defaults: ["pixelflinger-tests-mips"],
+
+ srcs: ["col32cb16blend_test.c"],
+}
diff --git a/libpixelflinger/tests/arch-mips/col32cb16blend/Android.mk b/libpixelflinger/tests/arch-mips/col32cb16blend/Android.mk
deleted file mode 100644
index 40f197f..0000000
--- a/libpixelflinger/tests/arch-mips/col32cb16blend/Android.mk
+++ /dev/null
@@ -1,18 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- col32cb16blend_test.c \
- ../../../arch-mips/col32cb16blend.S
-
-LOCAL_SHARED_LIBRARIES :=
-
-LOCAL_C_INCLUDES :=
-
-LOCAL_MODULE:= test-pixelflinger-mips-col32cb16blend
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_MULTILIB := 32
-
-include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-mips/t32cb16blend/Android.bp b/libpixelflinger/tests/arch-mips/t32cb16blend/Android.bp
new file mode 100644
index 0000000..069e97c
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips/t32cb16blend/Android.bp
@@ -0,0 +1,6 @@
+cc_test {
+ name: "test-pixelflinger-mips-t32cb16blend",
+ defaults: ["pixelflinger-tests-mips"],
+
+ srcs: ["t32cb16blend_test.c"],
+}
diff --git a/libpixelflinger/tests/arch-mips/t32cb16blend/Android.mk b/libpixelflinger/tests/arch-mips/t32cb16blend/Android.mk
deleted file mode 100644
index d0c0ae4..0000000
--- a/libpixelflinger/tests/arch-mips/t32cb16blend/Android.mk
+++ /dev/null
@@ -1,18 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- t32cb16blend_test.c \
- ../../../arch-mips/t32cb16blend.S
-
-LOCAL_SHARED_LIBRARIES :=
-
-LOCAL_C_INCLUDES :=
-
-LOCAL_MODULE:= test-pixelflinger-mips-t32cb16blend
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_MULTILIB := 32
-
-include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-mips64/Android.bp b/libpixelflinger/tests/arch-mips64/Android.bp
new file mode 100644
index 0000000..ba55d62
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips64/Android.bp
@@ -0,0 +1,11 @@
+cc_defaults {
+ name: "pixelflinger-tests-mips64",
+ defaults: ["pixelflinger-tests"],
+
+ enabled: false,
+ arch: {
+ mips64: {
+ enabled: true,
+ },
+ },
+}
diff --git a/libpixelflinger/tests/arch-mips64/Android.mk b/libpixelflinger/tests/arch-mips64/Android.mk
deleted file mode 100644
index 3b1c64e..0000000
--- a/libpixelflinger/tests/arch-mips64/Android.mk
+++ /dev/null
@@ -1,3 +0,0 @@
-ifeq ($(TARGET_ARCH),mips64)
-include $(all-subdir-makefiles)
-endif
diff --git a/libpixelflinger/tests/arch-mips64/assembler/Android.bp b/libpixelflinger/tests/arch-mips64/assembler/Android.bp
new file mode 100644
index 0000000..b672053
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips64/assembler/Android.bp
@@ -0,0 +1,9 @@
+cc_test {
+ name: "test-pixelflinger-mips64-assembler-test",
+ defaults: ["pixelflinger-tests-mips64"],
+
+ srcs: [
+ "mips64_assembler_test.cpp",
+ "asm_mips_test_jacket.S",
+ ],
+}
diff --git a/libpixelflinger/tests/arch-mips64/assembler/Android.mk b/libpixelflinger/tests/arch-mips64/assembler/Android.mk
deleted file mode 100644
index 4699961..0000000
--- a/libpixelflinger/tests/arch-mips64/assembler/Android.mk
+++ /dev/null
@@ -1,21 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- mips64_assembler_test.cpp\
- asm_mips_test_jacket.S
-
-LOCAL_SHARED_LIBRARIES := \
- libcutils \
- libpixelflinger
-
-LOCAL_C_INCLUDES := \
- $(LOCAL_PATH)/../../..
-
-LOCAL_MODULE:= test-pixelflinger-mips64-assembler-test
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_MULTILIB := 64
-
-include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-mips64/col32cb16blend/Android.bp b/libpixelflinger/tests/arch-mips64/col32cb16blend/Android.bp
new file mode 100644
index 0000000..bfc6ae9
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips64/col32cb16blend/Android.bp
@@ -0,0 +1,6 @@
+cc_test {
+ name: "test-pixelflinger-mips64-col32cb16blend",
+ defaults: ["pixelflinger-tests-mips64"],
+
+ srcs: ["col32cb16blend_test.c"],
+}
diff --git a/libpixelflinger/tests/arch-mips64/col32cb16blend/Android.mk b/libpixelflinger/tests/arch-mips64/col32cb16blend/Android.mk
deleted file mode 100644
index 7d4177e..0000000
--- a/libpixelflinger/tests/arch-mips64/col32cb16blend/Android.mk
+++ /dev/null
@@ -1,18 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- col32cb16blend_test.c \
- ../../../arch-mips64/col32cb16blend.S
-
-LOCAL_SHARED_LIBRARIES :=
-
-LOCAL_C_INCLUDES :=
-
-LOCAL_MODULE:= test-pixelflinger-mips64-col32cb16blend
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_MULTILIB := 64
-
-include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-mips64/disassembler/Android.bp b/libpixelflinger/tests/arch-mips64/disassembler/Android.bp
new file mode 100644
index 0000000..96bf9e9
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips64/disassembler/Android.bp
@@ -0,0 +1,6 @@
+cc_test {
+ name: "test-pixelflinger-mips64-disassembler-test",
+ defaults: ["pixelflinger-tests-mips64"],
+
+ srcs: ["mips64_disassembler_test.cpp"],
+}
diff --git a/libpixelflinger/tests/arch-mips64/disassembler/Android.mk b/libpixelflinger/tests/arch-mips64/disassembler/Android.mk
deleted file mode 100644
index 4e72b57..0000000
--- a/libpixelflinger/tests/arch-mips64/disassembler/Android.mk
+++ /dev/null
@@ -1,16 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- mips64_disassembler_test.cpp \
- ../../../codeflinger/mips64_disassem.c
-
-LOCAL_SHARED_LIBRARIES :=
-
-LOCAL_MODULE:= test-pixelflinger-mips64-disassembler-test
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_MULTILIB := 64
-
-include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/codegen/Android.bp b/libpixelflinger/tests/codegen/Android.bp
new file mode 100644
index 0000000..7e4bcfb
--- /dev/null
+++ b/libpixelflinger/tests/codegen/Android.bp
@@ -0,0 +1,12 @@
+cc_test {
+ name: "test-opengl-codegen",
+ defaults: ["pixelflinger-tests"],
+
+ srcs: ["codegen.cpp"],
+
+ arch: {
+ arm: {
+ instruction_set: "arm",
+ },
+ },
+}
diff --git a/libpixelflinger/tests/codegen/Android.mk b/libpixelflinger/tests/codegen/Android.mk
deleted file mode 100644
index 72d71ef..0000000
--- a/libpixelflinger/tests/codegen/Android.mk
+++ /dev/null
@@ -1,20 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- codegen.cpp.arm
-
-LOCAL_SHARED_LIBRARIES := \
- libcutils \
- libpixelflinger
-
-LOCAL_C_INCLUDES := \
- $(LOCAL_PATH)/../..
-
-LOCAL_MODULE:= test-opengl-codegen
-
-LOCAL_CFLAGS:= -Wall -Werror
-
-LOCAL_MODULE_TAGS := tests
-
-include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/gglmul/Android.bp b/libpixelflinger/tests/gglmul/Android.bp
new file mode 100644
index 0000000..288337b
--- /dev/null
+++ b/libpixelflinger/tests/gglmul/Android.bp
@@ -0,0 +1,12 @@
+cc_test {
+ name: "test-pixelflinger-gglmul",
+
+ srcs: ["gglmul_test.cpp"],
+
+ header_libs: ["libpixelflinger_internal"],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
diff --git a/libpixelflinger/tests/gglmul/Android.mk b/libpixelflinger/tests/gglmul/Android.mk
deleted file mode 100644
index 67f358f..0000000
--- a/libpixelflinger/tests/gglmul/Android.mk
+++ /dev/null
@@ -1,18 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- gglmul_test.cpp
-
-LOCAL_SHARED_LIBRARIES :=
-
-LOCAL_C_INCLUDES := \
- $(LOCAL_PATH)/../../include
-
-LOCAL_MODULE:= test-pixelflinger-gglmul
-
-LOCAL_CFLAGS:= -Wall -Werror
-
-LOCAL_MODULE_TAGS := tests
-
-include $(BUILD_NATIVE_TEST)
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
index 9fa4154..2412f3c 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -31,8 +31,10 @@
// that it only returns 0 in the case that the cgroup exists and it contains no processes.
int killProcessGroupOnce(uid_t uid, int initialPid, int signal);
-int createProcessGroup(uid_t uid, int initialPid);
+int createProcessGroup(uid_t uid, int initialPid, bool memControl = false);
+// Set various properties of a process group. For these functions to work, the process group must
+// have been created by passing memControl=true to createProcessGroup.
bool setProcessGroupSwappiness(uid_t uid, int initialPid, int swappiness);
bool setProcessGroupSoftLimit(uid_t uid, int initialPid, int64_t softLimitInBytes);
bool setProcessGroupLimit(uid_t uid, int initialPid, int64_t limitInBytes);
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 0a42053..9df8dd9 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -53,49 +53,31 @@
using namespace std::chrono_literals;
-#define MEM_CGROUP_PATH "/dev/memcg/apps"
-#define MEM_CGROUP_TASKS "/dev/memcg/apps/tasks"
-#define ACCT_CGROUP_PATH "/acct"
+static const char kCpuacctCgroup[] = "/acct";
+static const char kMemoryCgroup[] = "/dev/memcg/apps";
#define PROCESSGROUP_CGROUP_PROCS_FILE "/cgroup.procs"
-std::once_flag init_path_flag;
-
-static const std::string& GetCgroupRootPath() {
- static std::string cgroup_root_path;
- std::call_once(init_path_flag, [&]() {
- // low-ram devices use per-app memcg by default, unlike high-end ones
- bool low_ram_device = GetBoolProperty("ro.config.low_ram", false);
- bool per_app_memcg =
- GetBoolProperty("ro.config.per_app_memcg", low_ram_device);
- if (per_app_memcg) {
- // Check if mem cgroup is mounted, only then check for
- // write-access to avoid SELinux denials
- cgroup_root_path =
- (access(MEM_CGROUP_TASKS, F_OK) || access(MEM_CGROUP_PATH, W_OK) ?
- ACCT_CGROUP_PATH : MEM_CGROUP_PATH);
- } else {
- cgroup_root_path = ACCT_CGROUP_PATH;
- }
- });
- return cgroup_root_path;
+static bool isMemoryCgroupSupported() {
+ static bool memcg_supported = !access("/dev/memcg/memory.limit_in_bytes", F_OK);
+ return memcg_supported;
}
-static std::string ConvertUidToPath(uid_t uid) {
- return StringPrintf("%s/uid_%d", GetCgroupRootPath().c_str(), uid);
+static std::string ConvertUidToPath(const char* cgroup, uid_t uid) {
+ return StringPrintf("%s/uid_%d", cgroup, uid);
}
-static std::string ConvertUidPidToPath(uid_t uid, int pid) {
- return StringPrintf("%s/uid_%d/pid_%d", GetCgroupRootPath().c_str(), uid, pid);
+static std::string ConvertUidPidToPath(const char* cgroup, uid_t uid, int pid) {
+ return StringPrintf("%s/uid_%d/pid_%d", cgroup, uid, pid);
}
-static int RemoveProcessGroup(uid_t uid, int pid) {
+static int RemoveProcessGroup(const char* cgroup, uid_t uid, int pid) {
int ret;
- auto uid_pid_path = ConvertUidPidToPath(uid, pid);
+ auto uid_pid_path = ConvertUidPidToPath(cgroup, uid, pid);
ret = rmdir(uid_pid_path.c_str());
- auto uid_path = ConvertUidToPath(uid);
+ auto uid_path = ConvertUidToPath(cgroup, uid);
rmdir(uid_path.c_str());
return ret;
@@ -124,25 +106,26 @@
void removeAllProcessGroups()
{
LOG(VERBOSE) << "removeAllProcessGroups()";
- const auto& cgroup_root_path = GetCgroupRootPath();
- std::unique_ptr<DIR, decltype(&closedir)> root(opendir(cgroup_root_path.c_str()), closedir);
- if (root == NULL) {
- PLOG(ERROR) << "Failed to open " << cgroup_root_path;
- } else {
- dirent* dir;
- while ((dir = readdir(root.get())) != nullptr) {
- if (dir->d_type != DT_DIR) {
- continue;
- }
+ for (const char* cgroup_root_path : {kCpuacctCgroup, kMemoryCgroup}) {
+ std::unique_ptr<DIR, decltype(&closedir)> root(opendir(cgroup_root_path), closedir);
+ if (root == NULL) {
+ PLOG(ERROR) << "Failed to open " << cgroup_root_path;
+ } else {
+ dirent* dir;
+ while ((dir = readdir(root.get())) != nullptr) {
+ if (dir->d_type != DT_DIR) {
+ continue;
+ }
- if (!StartsWith(dir->d_name, "uid_")) {
- continue;
- }
+ if (!StartsWith(dir->d_name, "uid_")) {
+ continue;
+ }
- auto path = StringPrintf("%s/%s", cgroup_root_path.c_str(), dir->d_name);
- RemoveUidProcessGroups(path);
- LOG(VERBOSE) << "Removing " << path;
- if (rmdir(path.c_str()) == -1) PLOG(WARNING) << "Failed to remove " << path;
+ auto path = StringPrintf("%s/%s", cgroup_root_path, dir->d_name);
+ RemoveUidProcessGroups(path);
+ LOG(VERBOSE) << "Removing " << path;
+ if (rmdir(path.c_str()) == -1) PLOG(WARNING) << "Failed to remove " << path;
+ }
}
}
}
@@ -150,8 +133,8 @@
// Returns number of processes killed on success
// Returns 0 if there are no processes in the process cgroup left to kill
// Returns -1 on error
-static int DoKillProcessGroupOnce(uid_t uid, int initialPid, int signal) {
- auto path = ConvertUidPidToPath(uid, initialPid) + PROCESSGROUP_CGROUP_PROCS_FILE;
+static int DoKillProcessGroupOnce(const char* cgroup, uid_t uid, int initialPid, int signal) {
+ auto path = ConvertUidPidToPath(cgroup, uid, initialPid) + PROCESSGROUP_CGROUP_PROCS_FILE;
std::unique_ptr<FILE, decltype(&fclose)> fd(fopen(path.c_str(), "re"), fclose);
if (!fd) {
PLOG(WARNING) << "Failed to open process cgroup uid " << uid << " pid " << initialPid;
@@ -217,11 +200,16 @@
}
static int KillProcessGroup(uid_t uid, int initialPid, int signal, int retries) {
+ const char* cgroup =
+ (!access(ConvertUidPidToPath(kCpuacctCgroup, uid, initialPid).c_str(), F_OK))
+ ? kCpuacctCgroup
+ : kMemoryCgroup;
+
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
int retry = retries;
int processes;
- while ((processes = DoKillProcessGroupOnce(uid, initialPid, signal)) > 0) {
+ while ((processes = DoKillProcessGroupOnce(cgroup, uid, initialPid, signal)) > 0) {
LOG(VERBOSE) << "Killed " << processes << " processes for processgroup " << initialPid;
if (retry > 0) {
std::this_thread::sleep_for(5ms);
@@ -251,7 +239,7 @@
LOG(INFO) << "Successfully killed process cgroup uid " << uid << " pid " << initialPid
<< " in " << static_cast<int>(ms) << "ms";
}
- return RemoveProcessGroup(uid, initialPid);
+ return RemoveProcessGroup(cgroup, uid, initialPid);
} else {
if (retries > 0) {
LOG(ERROR) << "Failed to kill process cgroup uid " << uid << " pid " << initialPid
@@ -285,16 +273,29 @@
return true;
}
-int createProcessGroup(uid_t uid, int initialPid)
+static bool isPerAppMemcgEnabled() {
+ static bool per_app_memcg =
+ GetBoolProperty("ro.config.per_app_memcg", GetBoolProperty("ro.config.low_ram", false));
+ return per_app_memcg;
+}
+
+int createProcessGroup(uid_t uid, int initialPid, bool memControl)
{
- auto uid_path = ConvertUidToPath(uid);
+ const char* cgroup;
+ if (isMemoryCgroupSupported() && (memControl || isPerAppMemcgEnabled())) {
+ cgroup = kMemoryCgroup;
+ } else {
+ cgroup = kCpuacctCgroup;
+ }
+
+ auto uid_path = ConvertUidToPath(cgroup, uid);
if (!MkdirAndChown(uid_path, 0750, AID_SYSTEM, AID_SYSTEM)) {
PLOG(ERROR) << "Failed to make and chown " << uid_path;
return -errno;
}
- auto uid_pid_path = ConvertUidPidToPath(uid, initialPid);
+ auto uid_pid_path = ConvertUidPidToPath(cgroup, uid, initialPid);
if (!MkdirAndChown(uid_pid_path, 0750, AID_SYSTEM, AID_SYSTEM)) {
PLOG(ERROR) << "Failed to make and chown " << uid_pid_path;
@@ -313,12 +314,12 @@
}
static bool SetProcessGroupValue(uid_t uid, int pid, const std::string& file_name, int64_t value) {
- if (GetCgroupRootPath() != MEM_CGROUP_PATH) {
+ if (!isMemoryCgroupSupported()) {
PLOG(ERROR) << "Memcg is not mounted.";
return false;
}
- auto path = ConvertUidPidToPath(uid, pid) + file_name;
+ auto path = ConvertUidPidToPath(kMemoryCgroup, uid, pid) + file_name;
if (!WriteStringToFile(std::to_string(value), path)) {
PLOG(ERROR) << "Failed to write '" << value << "' to " << path;
diff --git a/libprocinfo/include/procinfo/process_map.h b/libprocinfo/include/procinfo/process_map.h
index 3771f9f..0fc4201 100644
--- a/libprocinfo/include/procinfo/process_map.h
+++ b/libprocinfo/include/procinfo/process_map.h
@@ -22,6 +22,7 @@
#include <functional>
#include <string>
+#include <vector>
#include <android-base/file.h>
@@ -147,5 +148,23 @@
return ReadMapFile("/proc/" + std::to_string(pid) + "/maps", callback);
}
+struct MapInfo {
+ uint64_t start;
+ uint64_t end;
+ uint16_t flags;
+ uint64_t pgoff;
+ std::string name;
+
+ MapInfo(uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, const char* name)
+ : start(start), end(end), flags(flags), pgoff(pgoff), name(name) {}
+};
+
+inline bool ReadProcessMaps(pid_t pid, std::vector<MapInfo>* maps) {
+ return ReadProcessMaps(
+ pid, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, const char* name) {
+ maps->emplace_back(start, end, flags, pgoff, name);
+ });
+}
+
} /* namespace procinfo */
} /* namespace android */
diff --git a/libprocinfo/process_map_benchmark.cpp b/libprocinfo/process_map_benchmark.cpp
index d9e8a4d..04995d4 100644
--- a/libprocinfo/process_map_benchmark.cpp
+++ b/libprocinfo/process_map_benchmark.cpp
@@ -27,21 +27,10 @@
#include <benchmark/benchmark.h>
-struct MapInfo {
- uint64_t start;
- uint64_t end;
- uint16_t flags;
- uint64_t pgoff;
- const std::string name;
-
- MapInfo(uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, const char* name)
- : start(start), end(end), flags(flags), pgoff(pgoff), name(name) {}
-};
-
static void BM_ReadMapFile(benchmark::State& state) {
std::string map_file = android::base::GetExecutableDirectory() + "/testdata/maps";
for (auto _ : state) {
- std::vector<MapInfo> maps;
+ std::vector<android::procinfo::MapInfo> maps;
android::procinfo::ReadMapFile(
map_file, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff,
const char* name) { maps.emplace_back(start, end, flags, pgoff, name); });
diff --git a/libprocinfo/process_map_test.cpp b/libprocinfo/process_map_test.cpp
index 4b93c5b..170a806 100644
--- a/libprocinfo/process_map_test.cpp
+++ b/libprocinfo/process_map_test.cpp
@@ -22,20 +22,9 @@
#include <gtest/gtest.h>
-struct MapInfo {
- uint64_t start;
- uint64_t end;
- uint16_t flags;
- uint64_t pgoff;
- const std::string name;
-
- MapInfo(uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, const char* name)
- : start(start), end(end), flags(flags), pgoff(pgoff), name(name) {}
-};
-
-TEST(process_map, smoke) {
+TEST(process_map, ReadMapFile) {
std::string map_file = android::base::GetExecutableDirectory() + "/testdata/maps";
- std::vector<MapInfo> maps;
+ std::vector<android::procinfo::MapInfo> maps;
ASSERT_TRUE(android::procinfo::ReadMapFile(
map_file, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff,
const char* name) { maps.emplace_back(start, end, flags, pgoff, name); }));
@@ -58,3 +47,14 @@
"[anon:dalvik-classes.dex extracted in memory from "
"/data/app/com.google.sample.tunnel-HGGRU03Gu1Mwkf_-RnFmvw==/base.apk]");
}
+
+TEST(process_map, ReadProcessMaps) {
+ std::vector<android::procinfo::MapInfo> maps;
+ ASSERT_TRUE(android::procinfo::ReadProcessMaps(
+ getpid(), [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff,
+ const char* name) { maps.emplace_back(start, end, flags, pgoff, name); }));
+ ASSERT_GT(maps.size(), 0u);
+ maps.clear();
+ ASSERT_TRUE(android::procinfo::ReadProcessMaps(getpid(), &maps));
+ ASSERT_GT(maps.size(), 0u);
+}
diff --git a/libunwindstack/DexFiles.cpp b/libunwindstack/DexFiles.cpp
index ac55fee..451a0b9 100644
--- a/libunwindstack/DexFiles.cpp
+++ b/libunwindstack/DexFiles.cpp
@@ -54,8 +54,8 @@
}
}
-void DexFiles::SetArch(ArchEnum arch) {
- switch (arch) {
+void DexFiles::ProcessArch() {
+ switch (arch()) {
case ARCH_ARM:
case ARCH_MIPS:
case ARCH_X86:
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 60ef796..4d72ead 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -88,6 +88,11 @@
}
}
+void Elf::Invalidate() {
+ interface_.reset(nullptr);
+ valid_ = false;
+}
+
bool Elf::GetSoname(std::string* name) {
std::lock_guard<std::mutex> guard(lock_);
return valid_ && interface_->GetSoname(name);
diff --git a/libunwindstack/Global.cpp b/libunwindstack/Global.cpp
index f0ad2b6..7a3de01 100644
--- a/libunwindstack/Global.cpp
+++ b/libunwindstack/Global.cpp
@@ -31,6 +31,13 @@
Global::Global(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs)
: memory_(memory), search_libs_(search_libs) {}
+void Global::SetArch(ArchEnum arch) {
+ if (arch_ == ARCH_UNKNOWN) {
+ arch_ = arch;
+ ProcessArch();
+ }
+}
+
uint64_t Global::GetVariableOffset(MapInfo* info, const std::string& variable) {
if (!search_libs_.empty()) {
bool found = false;
@@ -46,7 +53,7 @@
}
}
- Elf* elf = info->GetElf(memory_);
+ Elf* elf = info->GetElf(memory_, arch());
uint64_t ptr;
// Find first non-empty list (libraries might be loaded multiple times).
if (elf->GetGlobalVariable(variable, &ptr) && ptr != 0) {
diff --git a/libunwindstack/JitDebug.cpp b/libunwindstack/JitDebug.cpp
index d6af49e..20bc4b9 100644
--- a/libunwindstack/JitDebug.cpp
+++ b/libunwindstack/JitDebug.cpp
@@ -141,8 +141,8 @@
return code.next;
}
-void JitDebug::SetArch(ArchEnum arch) {
- switch (arch) {
+void JitDebug::ProcessArch() {
+ switch (arch()) {
case ARCH_X86:
read_descriptor_func_ = &JitDebug::ReadDescriptor32;
read_entry_func_ = &JitDebug::ReadEntry32Pack;
diff --git a/libunwindstack/LocalUnwinder.cpp b/libunwindstack/LocalUnwinder.cpp
index 31337a9..5b2fadf 100644
--- a/libunwindstack/LocalUnwinder.cpp
+++ b/libunwindstack/LocalUnwinder.cpp
@@ -88,6 +88,7 @@
bool LocalUnwinder::Unwind(std::vector<LocalFrameData>* frame_info, size_t max_frames) {
std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::CreateFromLocal());
unwindstack::RegsGetLocal(regs.get());
+ ArchEnum arch = regs->Arch();
size_t num_frames = 0;
bool adjust_pc = false;
@@ -100,7 +101,7 @@
break;
}
- Elf* elf = map_info->GetElf(process_memory_);
+ Elf* elf = map_info->GetElf(process_memory_, arch);
uint64_t rel_pc = elf->GetRelPc(cur_pc, map_info);
uint64_t step_pc = rel_pc;
uint64_t pc_adjustment;
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index 8527797..e3b48ca 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -96,11 +96,6 @@
}
}
- // If the map isn't readable, don't bother trying to read from process memory.
- if (!(flags & PROT_READ)) {
- return nullptr;
- }
-
// Need to verify that this elf is valid. It's possible that
// only part of the elf file to be mapped into memory is in the executable
// map. In this case, there will be another read-only map that includes the
@@ -132,21 +127,22 @@
}
}
- if (ro_map_info != nullptr) {
- // Make sure that relative pc values are corrected properly.
- elf_offset = offset - closest_offset;
-
- MemoryRanges* ranges = new MemoryRanges;
- ranges->Insert(new MemoryRange(process_memory, ro_map_info->start,
- ro_map_info->end - ro_map_info->start, 0));
- ranges->Insert(new MemoryRange(process_memory, start, end - start, elf_offset));
-
- return ranges;
+ if (ro_map_info == nullptr) {
+ return nullptr;
}
- return nullptr;
+
+ // Make sure that relative pc values are corrected properly.
+ elf_offset = offset - closest_offset;
+
+ MemoryRanges* ranges = new MemoryRanges;
+ ranges->Insert(new MemoryRange(process_memory, ro_map_info->start,
+ ro_map_info->end - ro_map_info->start, 0));
+ ranges->Insert(new MemoryRange(process_memory, start, end - start, elf_offset));
+
+ return ranges;
}
-Elf* MapInfo::GetElf(const std::shared_ptr<Memory>& process_memory) {
+Elf* MapInfo::GetElf(const std::shared_ptr<Memory>& process_memory, ArchEnum expected_arch) {
// Make sure no other thread is trying to add the elf to this map.
std::lock_guard<std::mutex> guard(mutex_);
@@ -176,6 +172,10 @@
// If the init fails, keep the elf around as an invalid object so we
// don't try to reinit the object.
elf->Init();
+ if (elf->valid() && expected_arch != elf->arch()) {
+ // Make the elf invalid, mismatch between arch and expected arch.
+ elf->Invalidate();
+ }
if (locked) {
Elf::CacheAdd(this);
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index b8b0ac6..b3c5549 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -135,6 +135,8 @@
last_error_.code = ERROR_NONE;
last_error_.address = 0;
+ ArchEnum arch = regs_->Arch();
+
bool return_address_attempt = false;
bool adjust_pc = false;
std::unique_ptr<JitDebug> jit_debug;
@@ -155,7 +157,7 @@
if (ShouldStop(map_suffixes_to_ignore, map_info->name)) {
break;
}
- elf = map_info->GetElf(process_memory_);
+ elf = map_info->GetElf(process_memory_, arch);
step_pc = regs_->pc();
rel_pc = elf->GetRelPc(step_pc, map_info);
// Everyone except elf data in gdb jit debug maps uses the relative pc.
diff --git a/libunwindstack/include/unwindstack/DexFiles.h b/libunwindstack/include/unwindstack/DexFiles.h
index c2fde74..c202a33 100644
--- a/libunwindstack/include/unwindstack/DexFiles.h
+++ b/libunwindstack/include/unwindstack/DexFiles.h
@@ -47,8 +47,6 @@
void GetMethodInformation(Maps* maps, MapInfo* info, uint64_t dex_pc, std::string* method_name,
uint64_t* method_offset);
- void SetArch(ArchEnum arch);
-
private:
void Init(Maps* maps);
@@ -64,6 +62,8 @@
bool ReadVariableData(uint64_t ptr_offset) override;
+ void ProcessArch() override;
+
std::mutex lock_;
bool initialized_ = false;
std::unordered_map<uint64_t, DexFile*> files_;
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index 24cabf2..e5b0a89 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -57,6 +57,8 @@
void InitGnuDebugdata();
+ void Invalidate();
+
bool GetSoname(std::string* name);
bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset);
diff --git a/libunwindstack/include/unwindstack/Global.h b/libunwindstack/include/unwindstack/Global.h
index 70e3ddd..a7e6c15 100644
--- a/libunwindstack/include/unwindstack/Global.h
+++ b/libunwindstack/include/unwindstack/Global.h
@@ -25,6 +25,7 @@
#include <unordered_map>
#include <vector>
+#include <unwindstack/Elf.h>
#include <unwindstack/Memory.h>
namespace unwindstack {
@@ -39,12 +40,20 @@
Global(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs);
virtual ~Global() = default;
+ void SetArch(ArchEnum arch);
+
+ ArchEnum arch() { return arch_; }
+
protected:
uint64_t GetVariableOffset(MapInfo* info, const std::string& variable);
void FindAndReadVariable(Maps* maps, const char* variable);
virtual bool ReadVariableData(uint64_t offset) = 0;
+ virtual void ProcessArch() = 0;
+
+ ArchEnum arch_ = ARCH_UNKNOWN;
+
std::shared_ptr<Memory> memory_;
std::vector<std::string> search_libs_;
};
diff --git a/libunwindstack/include/unwindstack/JitDebug.h b/libunwindstack/include/unwindstack/JitDebug.h
index ccb473f..f64b04f 100644
--- a/libunwindstack/include/unwindstack/JitDebug.h
+++ b/libunwindstack/include/unwindstack/JitDebug.h
@@ -42,17 +42,9 @@
Elf* GetElf(Maps* maps, uint64_t pc);
- void SetArch(ArchEnum arch);
-
private:
void Init(Maps* maps);
- uint64_t entry_addr_ = 0;
- bool initialized_ = false;
- std::vector<Elf*> elf_list_;
-
- std::mutex lock_;
-
uint64_t (JitDebug::*read_descriptor_func_)(uint64_t) = nullptr;
uint64_t (JitDebug::*read_entry_func_)(uint64_t*, uint64_t*) = nullptr;
@@ -64,6 +56,14 @@
uint64_t ReadEntry64(uint64_t* start, uint64_t* size);
bool ReadVariableData(uint64_t ptr_offset) override;
+
+ void ProcessArch() override;
+
+ uint64_t entry_addr_ = 0;
+ bool initialized_ = false;
+ std::vector<Elf*> elf_list_;
+
+ std::mutex lock_;
};
} // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h
index ff634f2..9c6b552 100644
--- a/libunwindstack/include/unwindstack/MapInfo.h
+++ b/libunwindstack/include/unwindstack/MapInfo.h
@@ -72,7 +72,7 @@
std::atomic_uint64_t load_bias;
// This function guarantees it will never return nullptr.
- Elf* GetElf(const std::shared_ptr<Memory>& process_memory);
+ Elf* GetElf(const std::shared_ptr<Memory>& process_memory, ArchEnum expected_arch);
uint64_t GetLoadBias(const std::shared_ptr<Memory>& process_memory);
diff --git a/libunwindstack/tests/DexFileTest.cpp b/libunwindstack/tests/DexFileTest.cpp
index 40f9f8e..95d2176 100644
--- a/libunwindstack/tests/DexFileTest.cpp
+++ b/libunwindstack/tests/DexFileTest.cpp
@@ -20,7 +20,7 @@
#include <unordered_map>
-#include <android-base/test_utils.h>
+#include <android-base/file.h>
#include <unwindstack/MapInfo.h>
#include <unwindstack/Memory.h>
diff --git a/libunwindstack/tests/DexFilesTest.cpp b/libunwindstack/tests/DexFilesTest.cpp
index 3ac3ca6..1ea9e5c 100644
--- a/libunwindstack/tests/DexFilesTest.cpp
+++ b/libunwindstack/tests/DexFilesTest.cpp
@@ -36,12 +36,20 @@
class DexFilesTest : public ::testing::Test {
protected:
- void SetUp() override {
- memory_ = new MemoryFake;
- process_memory_.reset(memory_);
+ void CreateFakeElf(MapInfo* map_info) {
+ MemoryFake* memory = new MemoryFake;
+ ElfFake* elf = new ElfFake(memory);
+ elf->FakeSetValid(true);
+ ElfInterfaceFake* interface = new ElfInterfaceFake(memory);
+ elf->FakeSetInterface(interface);
+ interface->FakeSetGlobalVariable("__dex_debug_descriptor", 0x800);
+ map_info->elf.reset(elf);
+ }
+
+ void Init(ArchEnum arch) {
dex_files_.reset(new DexFiles(process_memory_));
- dex_files_->SetArch(ARCH_ARM);
+ dex_files_->SetArch(arch);
maps_.reset(
new BufferMaps("1000-4000 ---s 00000000 00:00 0 /fake/elf\n"
@@ -58,35 +66,24 @@
// Global variable in a section that is not readable.
MapInfo* map_info = maps_->Get(kMapGlobalNonReadable);
ASSERT_TRUE(map_info != nullptr);
- MemoryFake* memory = new MemoryFake;
- ElfFake* elf = new ElfFake(memory);
- elf->FakeSetValid(true);
- ElfInterfaceFake* interface = new ElfInterfaceFake(memory);
- elf->FakeSetInterface(interface);
- interface->FakeSetGlobalVariable("__dex_debug_descriptor", 0x800);
- map_info->elf.reset(elf);
+ CreateFakeElf(map_info);
// Global variable not set by default.
map_info = maps_->Get(kMapGlobalSetToZero);
ASSERT_TRUE(map_info != nullptr);
- memory = new MemoryFake;
- elf = new ElfFake(memory);
- elf->FakeSetValid(true);
- interface = new ElfInterfaceFake(memory);
- elf->FakeSetInterface(interface);
- interface->FakeSetGlobalVariable("__dex_debug_descriptor", 0x800);
- map_info->elf.reset(elf);
+ CreateFakeElf(map_info);
// Global variable set in this map.
map_info = maps_->Get(kMapGlobal);
ASSERT_TRUE(map_info != nullptr);
- memory = new MemoryFake;
- elf = new ElfFake(memory);
- elf->FakeSetValid(true);
- interface = new ElfInterfaceFake(memory);
- elf->FakeSetInterface(interface);
- interface->FakeSetGlobalVariable("__dex_debug_descriptor", 0x800);
- map_info->elf.reset(elf);
+ CreateFakeElf(map_info);
+ }
+
+ void SetUp() override {
+ memory_ = new MemoryFake;
+ process_memory_.reset(memory_);
+
+ Init(ARCH_ARM);
}
void WriteDescriptor32(uint64_t addr, uint32_t head);
@@ -169,11 +166,12 @@
}
TEST_F(DexFilesTest, get_method_information_64) {
+ Init(ARCH_ARM64);
+
std::string method_name = "nothing";
uint64_t method_offset = 0x124;
MapInfo* info = maps_->Get(kMapDexFiles);
- dex_files_->SetArch(ARCH_ARM64);
WriteDescriptor64(0xf800, 0x200000);
WriteEntry64(0x200000, 0, 0, 0x301000);
WriteDex(0x301000);
@@ -199,11 +197,12 @@
}
TEST_F(DexFilesTest, get_method_information_not_first_entry_64) {
+ Init(ARCH_ARM64);
+
std::string method_name = "nothing";
uint64_t method_offset = 0x124;
MapInfo* info = maps_->Get(kMapDexFiles);
- dex_files_->SetArch(ARCH_ARM64);
WriteDescriptor64(0xf800, 0x200000);
WriteEntry64(0x200000, 0x200100, 0, 0x100000);
WriteEntry64(0x200100, 0, 0x200000, 0x300000);
@@ -297,6 +296,8 @@
}
TEST_F(DexFilesTest, get_method_information_global_skip_zero_64) {
+ Init(ARCH_ARM64);
+
std::string method_name = "nothing";
uint64_t method_offset = 0x124;
MapInfo* info = maps_->Get(kMapDexFiles);
@@ -308,7 +309,6 @@
WriteEntry64(0x200000, 0, 0, 0x300000);
WriteDex(0x300000);
- dex_files_->SetArch(ARCH_ARM64);
dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
EXPECT_EQ("Main.<init>", method_name);
EXPECT_EQ(0U, method_offset);
diff --git a/libunwindstack/tests/ElfCacheTest.cpp b/libunwindstack/tests/ElfCacheTest.cpp
index 8ed697c..07fd6f6 100644
--- a/libunwindstack/tests/ElfCacheTest.cpp
+++ b/libunwindstack/tests/ElfCacheTest.cpp
@@ -18,7 +18,6 @@
#include <unistd.h>
#include <android-base/file.h>
-#include <android-base/test_utils.h>
#include <gtest/gtest.h>
@@ -82,9 +81,9 @@
MapInfo info1(nullptr, start, end, 0, 0x5, tf.path);
MapInfo info2(nullptr, start, end, 0, 0x5, tf.path);
- Elf* elf1 = info1.GetElf(memory_);
+ Elf* elf1 = info1.GetElf(memory_, ARCH_ARM);
ASSERT_TRUE(elf1->valid());
- Elf* elf2 = info2.GetElf(memory_);
+ Elf* elf2 = info2.GetElf(memory_, ARCH_ARM);
ASSERT_TRUE(elf2->valid());
if (cache_enabled) {
@@ -132,10 +131,10 @@
MapInfo info300_1(nullptr, start, end, 0x300, 0x5, tf.path);
MapInfo info300_2(nullptr, start, end, 0x300, 0x5, tf.path);
- Elf* elf0_1 = info0_1.GetElf(memory_);
+ Elf* elf0_1 = info0_1.GetElf(memory_, ARCH_ARM);
ASSERT_TRUE(elf0_1->valid());
EXPECT_EQ(ARCH_ARM, elf0_1->arch());
- Elf* elf0_2 = info0_2.GetElf(memory_);
+ Elf* elf0_2 = info0_2.GetElf(memory_, ARCH_ARM);
ASSERT_TRUE(elf0_2->valid());
EXPECT_EQ(ARCH_ARM, elf0_2->arch());
EXPECT_EQ(0U, info0_1.elf_offset);
@@ -146,10 +145,10 @@
EXPECT_NE(elf0_1, elf0_2);
}
- Elf* elf100_1 = info100_1.GetElf(memory_);
+ Elf* elf100_1 = info100_1.GetElf(memory_, ARCH_X86);
ASSERT_TRUE(elf100_1->valid());
EXPECT_EQ(ARCH_X86, elf100_1->arch());
- Elf* elf100_2 = info100_2.GetElf(memory_);
+ Elf* elf100_2 = info100_2.GetElf(memory_, ARCH_X86);
ASSERT_TRUE(elf100_2->valid());
EXPECT_EQ(ARCH_X86, elf100_2->arch());
EXPECT_EQ(0U, info100_1.elf_offset);
@@ -160,10 +159,10 @@
EXPECT_NE(elf100_1, elf100_2);
}
- Elf* elf200_1 = info200_1.GetElf(memory_);
+ Elf* elf200_1 = info200_1.GetElf(memory_, ARCH_X86_64);
ASSERT_TRUE(elf200_1->valid());
EXPECT_EQ(ARCH_X86_64, elf200_1->arch());
- Elf* elf200_2 = info200_2.GetElf(memory_);
+ Elf* elf200_2 = info200_2.GetElf(memory_, ARCH_X86_64);
ASSERT_TRUE(elf200_2->valid());
EXPECT_EQ(ARCH_X86_64, elf200_2->arch());
EXPECT_EQ(0U, info200_1.elf_offset);
@@ -174,10 +173,10 @@
EXPECT_NE(elf200_1, elf200_2);
}
- Elf* elf300_1 = info300_1.GetElf(memory_);
+ Elf* elf300_1 = info300_1.GetElf(memory_, ARCH_ARM);
ASSERT_TRUE(elf300_1->valid());
EXPECT_EQ(ARCH_ARM, elf300_1->arch());
- Elf* elf300_2 = info300_2.GetElf(memory_);
+ Elf* elf300_2 = info300_2.GetElf(memory_, ARCH_ARM);
ASSERT_TRUE(elf300_2->valid());
EXPECT_EQ(ARCH_ARM, elf300_2->arch());
EXPECT_EQ(0x300U, info300_1.elf_offset);
@@ -222,10 +221,10 @@
MapInfo info400_1(nullptr, start, end, 0x400, 0x5, tf.path);
MapInfo info400_2(nullptr, start, end, 0x400, 0x5, tf.path);
- Elf* elf300_1 = info300_1.GetElf(memory_);
+ Elf* elf300_1 = info300_1.GetElf(memory_, ARCH_ARM);
ASSERT_TRUE(elf300_1->valid());
EXPECT_EQ(ARCH_ARM, elf300_1->arch());
- Elf* elf300_2 = info300_2.GetElf(memory_);
+ Elf* elf300_2 = info300_2.GetElf(memory_, ARCH_ARM);
ASSERT_TRUE(elf300_2->valid());
EXPECT_EQ(ARCH_ARM, elf300_2->arch());
EXPECT_EQ(0x300U, info300_1.elf_offset);
@@ -236,10 +235,10 @@
EXPECT_NE(elf300_1, elf300_2);
}
- Elf* elf400_1 = info400_1.GetElf(memory_);
+ Elf* elf400_1 = info400_1.GetElf(memory_, ARCH_ARM);
ASSERT_TRUE(elf400_1->valid());
EXPECT_EQ(ARCH_ARM, elf400_1->arch());
- Elf* elf400_2 = info400_2.GetElf(memory_);
+ Elf* elf400_2 = info400_2.GetElf(memory_, ARCH_ARM);
ASSERT_TRUE(elf400_2->valid());
EXPECT_EQ(ARCH_ARM, elf400_2->arch());
EXPECT_EQ(0x400U, info400_1.elf_offset);
diff --git a/libunwindstack/tests/JitDebugTest.cpp b/libunwindstack/tests/JitDebugTest.cpp
index 4598526..b1ca111 100644
--- a/libunwindstack/tests/JitDebugTest.cpp
+++ b/libunwindstack/tests/JitDebugTest.cpp
@@ -35,12 +35,19 @@
class JitDebugTest : public ::testing::Test {
protected:
- void SetUp() override {
- memory_ = new MemoryFake;
- process_memory_.reset(memory_);
+ void CreateFakeElf(MapInfo* map_info) {
+ MemoryFake* memory = new MemoryFake;
+ ElfFake* elf = new ElfFake(memory);
+ elf->FakeSetValid(true);
+ ElfInterfaceFake* interface = new ElfInterfaceFake(memory);
+ elf->FakeSetInterface(interface);
+ interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800);
+ map_info->elf.reset(elf);
+ }
+ void Init(ArchEnum arch) {
jit_debug_.reset(new JitDebug(process_memory_));
- jit_debug_->SetArch(ARCH_ARM);
+ jit_debug_->SetArch(arch);
maps_.reset(
new BufferMaps("1000-4000 ---s 00000000 00:00 0 /fake/elf1\n"
@@ -57,33 +64,22 @@
MapInfo* map_info = maps_->Get(3);
ASSERT_TRUE(map_info != nullptr);
- MemoryFake* memory = new MemoryFake;
- ElfFake* elf = new ElfFake(memory);
- elf->FakeSetValid(true);
- ElfInterfaceFake* interface = new ElfInterfaceFake(memory);
- elf->FakeSetInterface(interface);
- interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800);
- map_info->elf.reset(elf);
+ CreateFakeElf(map_info);
map_info = maps_->Get(5);
ASSERT_TRUE(map_info != nullptr);
- memory = new MemoryFake;
- elf = new ElfFake(memory);
- elf->FakeSetValid(true);
- interface = new ElfInterfaceFake(memory);
- elf->FakeSetInterface(interface);
- interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800);
- map_info->elf.reset(elf);
+ CreateFakeElf(map_info);
map_info = maps_->Get(7);
ASSERT_TRUE(map_info != nullptr);
- memory = new MemoryFake;
- elf = new ElfFake(memory);
- elf->FakeSetValid(true);
- interface = new ElfInterfaceFake(memory);
- elf->FakeSetInterface(interface);
- interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800);
- map_info->elf.reset(elf);
+ CreateFakeElf(map_info);
+ }
+
+ void SetUp() override {
+ memory_ = new MemoryFake;
+ process_memory_.reset(memory_);
+
+ Init(ARCH_ARM);
}
template <typename EhdrType, typename ShdrType>
@@ -326,6 +322,8 @@
}
TEST_F(JitDebugTest, get_elf_x86) {
+ Init(ARCH_X86);
+
CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
WriteDescriptor32(0xf800, 0x200000);
@@ -343,12 +341,13 @@
}
TEST_F(JitDebugTest, get_elf_64) {
+ Init(ARCH_ARM64);
+
CreateElf<Elf64_Ehdr, Elf64_Shdr>(0x4000, ELFCLASS64, EM_AARCH64, 0x1500, 0x200);
WriteDescriptor64(0xf800, 0x200000);
WriteEntry64(0x200000, 0, 0, 0x4000, 0x1000);
- jit_debug_->SetArch(ARCH_ARM64);
Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
ASSERT_TRUE(elf != nullptr);
diff --git a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
index 2a73c7e..0987bc1 100644
--- a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
+++ b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
@@ -27,7 +27,6 @@
#include <vector>
#include <android-base/file.h>
-#include <android-base/test_utils.h>
#include <gtest/gtest.h>
#include <unwindstack/Elf.h>
diff --git a/libunwindstack/tests/MapInfoGetElfTest.cpp b/libunwindstack/tests/MapInfoGetElfTest.cpp
index c6c1c34..99f8fa3 100644
--- a/libunwindstack/tests/MapInfoGetElfTest.cpp
+++ b/libunwindstack/tests/MapInfoGetElfTest.cpp
@@ -29,7 +29,6 @@
#include <vector>
#include <android-base/file.h>
-#include <android-base/test_utils.h>
#include <gtest/gtest.h>
#include <unwindstack/Elf.h>
@@ -72,7 +71,7 @@
MapInfo info(nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
// The map is empty, but this should still create an invalid elf object.
- Elf* elf = info.GetElf(process_memory_);
+ Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
ASSERT_TRUE(elf != nullptr);
ASSERT_FALSE(elf->valid());
}
@@ -84,7 +83,7 @@
TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
memory_->SetMemory(0x3000, &ehdr, sizeof(ehdr));
- Elf* elf = info.GetElf(process_memory_);
+ Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
ASSERT_TRUE(elf != nullptr);
ASSERT_TRUE(elf->valid());
EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
@@ -98,13 +97,25 @@
TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64);
memory_->SetMemory(0x8000, &ehdr, sizeof(ehdr));
- Elf* elf = info.GetElf(process_memory_);
+ Elf* elf = info.GetElf(process_memory_, ARCH_ARM64);
ASSERT_TRUE(elf != nullptr);
ASSERT_TRUE(elf->valid());
EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
EXPECT_EQ(ELFCLASS64, elf->class_type());
}
+TEST_F(MapInfoGetElfTest, invalid_arch_mismatch) {
+ MapInfo info(nullptr, 0x3000, 0x4000, 0, PROT_READ, "");
+
+ Elf32_Ehdr ehdr;
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+ memory_->SetMemory(0x3000, &ehdr, sizeof(ehdr));
+
+ Elf* elf = info.GetElf(process_memory_, ARCH_X86);
+ ASSERT_TRUE(elf != nullptr);
+ ASSERT_FALSE(elf->valid());
+}
+
TEST_F(MapInfoGetElfTest, gnu_debugdata_init32) {
MapInfo info(nullptr, 0x2000, 0x3000, 0, PROT_READ, "");
@@ -113,7 +124,7 @@
memory_->SetMemory(0x2000 + offset, ptr, size);
});
- Elf* elf = info.GetElf(process_memory_);
+ Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
ASSERT_TRUE(elf != nullptr);
ASSERT_TRUE(elf->valid());
EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
@@ -129,7 +140,7 @@
memory_->SetMemory(0x5000 + offset, ptr, size);
});
- Elf* elf = info.GetElf(process_memory_);
+ Elf* elf = info.GetElf(process_memory_, ARCH_ARM64);
ASSERT_TRUE(elf != nullptr);
ASSERT_TRUE(elf->valid());
EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
@@ -144,20 +155,20 @@
TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
ASSERT_TRUE(android::base::WriteFully(elf_.fd, &ehdr, sizeof(ehdr)));
- Elf* elf = info.GetElf(process_memory_);
+ Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
ASSERT_TRUE(elf != nullptr);
ASSERT_FALSE(elf->valid());
info.elf.reset();
info.end = 0xfff;
- elf = info.GetElf(process_memory_);
+ elf = info.GetElf(process_memory_, ARCH_ARM);
ASSERT_TRUE(elf != nullptr);
ASSERT_FALSE(elf->valid());
// Make sure this test is valid.
info.elf.reset();
info.end = 0x2000;
- elf = info.GetElf(process_memory_);
+ elf = info.GetElf(process_memory_, ARCH_ARM);
ASSERT_TRUE(elf != nullptr);
ASSERT_TRUE(elf->valid());
}
@@ -174,7 +185,7 @@
memcpy(buffer.data(), &ehdr, sizeof(ehdr));
ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
- Elf* elf = info.GetElf(process_memory_);
+ Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
ASSERT_TRUE(elf != nullptr);
ASSERT_TRUE(elf->valid());
ASSERT_TRUE(elf->memory() != nullptr);
@@ -203,7 +214,7 @@
memcpy(&buffer[info.offset], &ehdr, sizeof(ehdr));
ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
- Elf* elf = info.GetElf(process_memory_);
+ Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
ASSERT_TRUE(elf != nullptr);
ASSERT_TRUE(elf->valid());
ASSERT_TRUE(elf->memory() != nullptr);
@@ -236,7 +247,7 @@
memcpy(&buffer[info.offset], &ehdr, sizeof(ehdr));
ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
- Elf* elf = info.GetElf(process_memory_);
+ Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
ASSERT_TRUE(elf != nullptr);
ASSERT_TRUE(elf->valid());
ASSERT_TRUE(elf->memory() != nullptr);
@@ -264,7 +275,7 @@
memcpy(&buffer[info.offset], &ehdr, sizeof(ehdr));
ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
- Elf* elf = info.GetElf(process_memory_);
+ Elf* elf = info.GetElf(process_memory_, ARCH_ARM64);
ASSERT_TRUE(elf != nullptr);
ASSERT_TRUE(elf->valid());
ASSERT_TRUE(elf->memory() != nullptr);
@@ -279,27 +290,6 @@
ASSERT_TRUE(elf->memory()->ReadFully(0x1000, buffer.data(), 1));
}
-TEST_F(MapInfoGetElfTest, process_memory_not_read_only) {
- MapInfo info(nullptr, 0x9000, 0xa000, 0x1000, 0, "");
-
- // Create valid elf data in process memory only.
- Elf64_Ehdr ehdr;
- TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64);
- ehdr.e_shoff = 0x2000;
- ehdr.e_shentsize = sizeof(Elf64_Shdr) + 100;
- ehdr.e_shnum = 0;
- memory_->SetMemory(0x9000, &ehdr, sizeof(ehdr));
-
- Elf* elf = info.GetElf(process_memory_);
- ASSERT_TRUE(elf != nullptr);
- ASSERT_FALSE(elf->valid());
-
- info.elf.reset();
- info.flags = PROT_READ;
- elf = info.GetElf(process_memory_);
- ASSERT_TRUE(elf->valid());
-}
-
TEST_F(MapInfoGetElfTest, check_device_maps) {
MapInfo info(nullptr, 0x7000, 0x8000, 0x1000, PROT_READ | MAPS_FLAGS_DEVICE_MAP,
"/dev/something");
@@ -313,20 +303,20 @@
ehdr.e_shnum = 0;
memory_->SetMemory(0x7000, &ehdr, sizeof(ehdr));
- Elf* elf = info.GetElf(process_memory_);
+ Elf* elf = info.GetElf(process_memory_, ARCH_X86_64);
ASSERT_TRUE(elf != nullptr);
ASSERT_FALSE(elf->valid());
// Set the name to nothing to verify that it still fails.
info.elf.reset();
info.name = "";
- elf = info.GetElf(process_memory_);
+ elf = info.GetElf(process_memory_, ARCH_X86_64);
ASSERT_FALSE(elf->valid());
// Change the flags and verify the elf is valid now.
info.elf.reset();
info.flags = PROT_READ;
- elf = info.GetElf(process_memory_);
+ elf = info.GetElf(process_memory_, ARCH_X86_64);
ASSERT_TRUE(elf->valid());
}
@@ -352,7 +342,7 @@
std::thread* thread = new std::thread([i, this, &wait, &info, &elf_in_threads]() {
while (wait)
;
- Elf* elf = info.GetElf(process_memory_);
+ Elf* elf = info.GetElf(process_memory_, ARCH_X86_64);
elf_in_threads[i] = elf;
});
threads.push_back(thread);
diff --git a/libunwindstack/tests/MapsTest.cpp b/libunwindstack/tests/MapsTest.cpp
index 6bdd0b2..80e292a 100644
--- a/libunwindstack/tests/MapsTest.cpp
+++ b/libunwindstack/tests/MapsTest.cpp
@@ -19,7 +19,6 @@
#include <android-base/file.h>
#include <android-base/stringprintf.h>
-#include <android-base/test_utils.h>
#include <gtest/gtest.h>
#include <unwindstack/Maps.h>
diff --git a/libunwindstack/tests/MemoryOfflineTest.cpp b/libunwindstack/tests/MemoryOfflineTest.cpp
index 14d58e6..ab9aa9d 100644
--- a/libunwindstack/tests/MemoryOfflineTest.cpp
+++ b/libunwindstack/tests/MemoryOfflineTest.cpp
@@ -19,7 +19,6 @@
#include <gtest/gtest.h>
#include <android-base/file.h>
-#include <android-base/test_utils.h>
#include <unwindstack/Memory.h>
namespace unwindstack {
diff --git a/libutils/include/utils/Thread.h b/libutils/include/utils/Thread.h
index 3525138..fc67656 100644
--- a/libutils/include/utils/Thread.h
+++ b/libutils/include/utils/Thread.h
@@ -47,6 +47,7 @@
virtual ~Thread();
// Start the thread in threadLoop() which needs to be implemented.
+ // NOLINTNEXTLINE(google-default-arguments)
virtual status_t run( const char* name,
int32_t priority = PRIORITY_DEFAULT,
size_t stack = 0);
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index af9f4c8..cea42d4 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -28,7 +28,6 @@
#include <android-base/file.h>
#include <android-base/mapped_file.h>
-#include <android-base/test_utils.h>
#include <android-base/unique_fd.h>
#include <gtest/gtest.h>
#include <ziparchive/zip_archive.h>
@@ -298,7 +297,7 @@
ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, kEmptyEntriesZip, sizeof(kEmptyEntriesZip)));
ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle));
+ ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle, false));
ZipEntry entry;
ZipString empty_name;
@@ -323,7 +322,7 @@
ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, reinterpret_cast<const uint8_t*>(kAbZip),
sizeof(kAbZip) - 1));
ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EntryLargerThan32KTest", &handle));
+ ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EntryLargerThan32KTest", &handle, false));
ZipEntry entry;
ZipString ab_name;
@@ -348,7 +347,7 @@
// Read the file back to a buffer and make sure the contents are
// the same as the memory buffer we extracted directly to.
std::vector<uint8_t> file_contents(kAbUncompressedSize);
- ASSERT_EQ(0, lseek64(tmp_output_file.fd, 0, SEEK_SET));
+ ASSERT_EQ(0, lseek(tmp_output_file.fd, 0, SEEK_SET));
ASSERT_TRUE(android::base::ReadFully(tmp_output_file.fd, &file_contents[0], file_contents.size()));
ASSERT_EQ(file_contents, buffer);
@@ -370,7 +369,7 @@
ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, trailer, sizeof(trailer)));
ZipArchiveHandle handle;
- ASSERT_GT(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle));
+ ASSERT_GT(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle, false));
}
TEST(ziparchive, ExtractToFile) {
@@ -392,7 +391,7 @@
// Assert that the first 8 bytes of the file haven't been clobbered.
uint8_t read_buffer[data_size];
- ASSERT_EQ(0, lseek64(tmp_file.fd, 0, SEEK_SET));
+ ASSERT_EQ(0, lseek(tmp_file.fd, 0, SEEK_SET));
ASSERT_TRUE(android::base::ReadFully(tmp_file.fd, read_buffer, data_size));
ASSERT_EQ(0, memcmp(read_buffer, data, data_size));
@@ -404,7 +403,7 @@
// Assert that the total length of the file is sane
ASSERT_EQ(static_cast<ssize_t>(data_size + kATxtContents.size()),
- lseek64(tmp_file.fd, 0, SEEK_END));
+ lseek(tmp_file.fd, 0, SEEK_END));
}
#if !defined(_WIN32)
@@ -580,7 +579,7 @@
ASSERT_NE(-1, tmp_file.fd);
ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, &zip_data[0], zip_data.size()));
ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "ExtractEntryToMemory", &handle));
+ ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "ExtractEntryToMemory", &handle, false));
// This function expects a variant of kDataDescriptorZipFile, for look for
// an entry whose name is "name" and whose size is 12 (contents =
@@ -688,7 +687,7 @@
ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, &kZipFileWithBrokenLfhSignature[0],
kZipFileWithBrokenLfhSignature.size()));
ZipArchiveHandle handle;
- ASSERT_EQ(-1, OpenArchiveFd(tmp_file.fd, "LeadingNonZipBytes", &handle));
+ ASSERT_EQ(-1, OpenArchiveFd(tmp_file.fd, "LeadingNonZipBytes", &handle, false));
}
class VectorReader : public zip_archive::Reader {
diff --git a/llkd/README.md b/llkd/README.md
index e5be850..3da7a2f 100644
--- a/llkd/README.md
+++ b/llkd/README.md
@@ -44,7 +44,8 @@
ABA detection since forward scheduling progress is allowed, thus the condition
for the symbols are:
-- Check is looking for " " + __symbol__+ "0x" in /proc/<pid>/stack.
+- Check is looking for " __symbol__+0x" or " __symbol__.cfi+0x" in
+ /proc/__pid__/stack.
- The __symbol__ should be rare and short lived enough that on a typical
system the function is seen at most only once in a sample over the timeout
period of ro.llk.stack.timeout_ms, samples occur every ro.llk.check_ms. This
@@ -88,7 +89,14 @@
Android Properties llkd respond to (*prop*_ms parms are in milliseconds):
#### ro.config.low_ram
-default false, if true do not sysrq t (dump all threads).
+device is configured with limited memory.
+
+#### ro.debuggable
+device is configured for userdebug or eng build.
+
+#### ro.llk.sysrq_t
+default not ro.config.low_ram, or ro.debuggable if property is "eng".
+if true do sysrq t (dump all threads).
#### ro.llk.enable
default false, allow live-lock daemon to be enabled.
@@ -121,14 +129,14 @@
#### ro.llk.stack.timeout_ms
default ro.llk.timeout_ms,
checking for persistent stack symbols maximum timelimit.
-Only active on userdebug and eng builds.
+Only active on userdebug or eng builds.
#### ro.llk.check_ms
default 2 minutes samples of threads for D or Z.
#### ro.llk.stack
-default cma_alloc,__get_user_pages, comma separated list of kernel symbols.
-The string "*false*" is the equivalent to an *empty* list.
+default cma_alloc,__get_user_pages,bit_wait_io comma separated list of kernel
+symbols. The string "*false*" is the equivalent to an *empty* list.
Look for kernel stack symbols that if ever persistently present can
indicate a subsystem is locked up.
Beware, check does not on purpose do forward scheduling ABA except by polling
@@ -136,11 +144,14 @@
should be exceptionally rare and fleeting.
One must be convinced that it is virtually *impossible* for symbol to show up
persistently in all samples of the stack.
-Only active on userdebug and eng builds.
+Again, looks for a match for either " **symbol**+0x" or " **symbol**.cfi+0x"
+in stack expansion.
+Only available on userdebug or eng builds, limited privileges due to security
+concerns on user builds prevents this checking.
#### ro.llk.blacklist.process
default 0,1,2 (kernel, init and [kthreadd]) plus process names
-init,[kthreadd],[khungtaskd],lmkd,lmkd.llkd,llkd,watchdogd,
+init,[kthreadd],[khungtaskd],lmkd,llkd,watchdogd,
[watchdogd],[watchdogd/0],...,[watchdogd/***get_nprocs**-1*].
The string "*false*" is the equivalent to an *empty* list.
Do not watch these processes. A process can be comm, cmdline or pid reference.
@@ -160,7 +171,7 @@
Do not watch processes that match this uid.
#### ro.llk.blacklist.process.stack
-default process names init,lmkd,lmkd.llkd,llkd,keystore,logd.
+default process names init,lmkd.llkd,llkd,keystore,ueventd,apexd,logd.
The string "*false*" is the equivalent to an *empty* list.
This subset of processes are not monitored for live lock stack signatures.
Also prevents the sepolicy violation associated with processes that block
diff --git a/llkd/include/llkd.h b/llkd/include/llkd.h
index 2c62fca..b16b1d8 100644
--- a/llkd/include/llkd.h
+++ b/llkd/include/llkd.h
@@ -35,6 +35,8 @@
#define LLK_ENABLE_DEFAULT false /* "eng" and userdebug true */
#define KHT_ENABLE_WRITEABLE_PROPERTY "khungtask.enable"
#define KHT_ENABLE_PROPERTY "ro." KHT_ENABLE_WRITEABLE_PROPERTY
+#define LLK_ENABLE_SYSRQ_T_PROPERTY "ro.llk.sysrq_t"
+#define LLK_ENABLE_SYSRQ_T_DEFAULT true
#define LLK_MLOCKALL_PROPERTY "ro.llk.mlockall"
#define LLK_MLOCKALL_DEFAULT true
#define LLK_KILLTEST_PROPERTY "ro.llk.killtest"
@@ -48,7 +50,7 @@
/* LLK_CHECK_MS_DEFAULT = actual timeout_ms / LLK_CHECKS_PER_TIMEOUT_DEFAULT */
#define LLK_CHECKS_PER_TIMEOUT_DEFAULT 5
#define LLK_CHECK_STACK_PROPERTY "ro.llk.stack"
-#define LLK_CHECK_STACK_DEFAULT "cma_alloc,__get_user_pages"
+#define LLK_CHECK_STACK_DEFAULT "cma_alloc,__get_user_pages,bit_wait_io"
#define LLK_BLACKLIST_PROCESS_PROPERTY "ro.llk.blacklist.process"
#define LLK_BLACKLIST_PROCESS_DEFAULT \
"0,1,2,init,[kthreadd],[khungtaskd],lmkd,llkd,watchdogd,[watchdogd],[watchdogd/0]"
diff --git a/llkd/libllkd.cpp b/llkd/libllkd.cpp
index 2727aab..427dace 100644
--- a/llkd/libllkd.cpp
+++ b/llkd/libllkd.cpp
@@ -85,6 +85,7 @@
milliseconds llkCheckMs; // checking interval to inspect any
// persistent live-locked states
bool llkLowRam; // ro.config.low_ram
+bool llkEnableSysrqT = LLK_ENABLE_SYSRQ_T_DEFAULT; // sysrq stack trace dump
bool khtEnable = LLK_ENABLE_DEFAULT; // [khungtaskd] panic
// [khungtaskd] should have a timeout beyond the granularity of llkTimeoutMs.
// Provides a wide angle of margin b/c khtTimeout is also its granularity.
@@ -509,8 +510,10 @@
return android::base::Trim(content) == string;
}
-void llkPanicKernel(bool dump, pid_t tid, const char* state) __noreturn;
-void llkPanicKernel(bool dump, pid_t tid, const char* state) {
+void llkPanicKernel(bool dump, pid_t tid, const char* state,
+ const std::string& message = "") __noreturn;
+void llkPanicKernel(bool dump, pid_t tid, const char* state, const std::string& message) {
+ if (!message.empty()) LOG(ERROR) << message;
auto sysrqTriggerFd = llkFileToWriteFd("/proc/sysrq-trigger");
if (sysrqTriggerFd < 0) {
// DYB
@@ -523,14 +526,24 @@
if (dump) {
// Show all locks that are held
android::base::WriteStringToFd("d", sysrqTriggerFd);
+ // Show all waiting tasks
+ android::base::WriteStringToFd("w", sysrqTriggerFd);
// This can trigger hardware watchdog, that is somewhat _ok_.
// But useless if pstore configured for <256KB, low ram devices ...
- if (!llkLowRam) {
+ if (llkEnableSysrqT) {
android::base::WriteStringToFd("t", sysrqTriggerFd);
+ // Show all locks that are held (in case 't' overflows ramoops)
+ android::base::WriteStringToFd("d", sysrqTriggerFd);
+ // Show all waiting tasks (in case 't' overflows ramoops)
+ android::base::WriteStringToFd("w", sysrqTriggerFd);
}
::usleep(200000); // let everything settle
}
- llkWriteStringToFile("SysRq : Trigger a crash : 'livelock,"s + state + "'\n", "/dev/kmsg");
+ // SysRq message matches kernel format, and propagates through bootstat
+ // ultimately to the boot reason into panic,livelock,<state>.
+ llkWriteStringToFile(message + (message.empty() ? "" : "\n") +
+ "SysRq : Trigger a crash : 'livelock,"s + state + "'\n",
+ "/dev/kmsg");
android::base::WriteStringToFd("c", sysrqTriggerFd);
// NOTREACHED
// DYB
@@ -542,7 +555,9 @@
}
void llkAlarmHandler(int) {
- llkPanicKernel(false, ::getpid(), "alarm");
+ LOG(FATAL) << "alarm";
+ // NOTREACHED
+ llkPanicKernel(true, ::getpid(), "alarm");
}
milliseconds GetUintProperty(const std::string& key, milliseconds def) {
@@ -726,7 +741,8 @@
char match = -1;
for (const auto& stack : llkCheckStackSymbols) {
if (++idx < 0) break;
- if (kernel_stack.find(" "s + stack + "+0x") != std::string::npos) {
+ if ((kernel_stack.find(" "s + stack + "+0x") != std::string::npos) ||
+ (kernel_stack.find(" "s + stack + ".cfi+0x") != std::string::npos)) {
match = idx;
break;
}
@@ -798,6 +814,7 @@
void llkLogConfig(void) {
LOG(INFO) << "ro.config.low_ram=" << llkFormat(llkLowRam) << "\n"
+ << LLK_ENABLE_SYSRQ_T_PROPERTY "=" << llkFormat(llkEnableSysrqT) << "\n"
<< LLK_ENABLE_PROPERTY "=" << llkFormat(llkEnable) << "\n"
<< KHT_ENABLE_PROPERTY "=" << llkFormat(khtEnable) << "\n"
<< LLK_MLOCKALL_PROPERTY "=" << llkFormat(llkMlockall) << "\n"
@@ -1089,10 +1106,12 @@
}
}
// We are here because we have confirmed kernel live-lock
- LOG(ERROR) << state << ' ' << llkFormat(procp->count) << ' ' << ppid << "->" << pid
- << "->" << tid << ' ' << procp->getComm() << " [panic]";
+ const auto message = state + " "s + llkFormat(procp->count) + " " +
+ std::to_string(ppid) + "->" + std::to_string(pid) + "->" +
+ std::to_string(tid) + " " + procp->getComm() + " [panic]";
llkPanicKernel(true, tid,
- (state == 'Z') ? "zombie" : (state == 'D') ? "driver" : "sleeping");
+ (state == 'Z') ? "zombie" : (state == 'D') ? "driver" : "sleeping",
+ message);
}
LOG(VERBOSE) << "+closedir()";
}
@@ -1149,13 +1168,22 @@
return duration_cast<milliseconds>(llkCheck()).count();
}
+bool llkCheckEng(const std::string& property) {
+ return android::base::GetProperty(property, "eng") == "eng";
+}
+
bool llkInit(const char* threadname) {
auto debuggable = android::base::GetBoolProperty("ro.debuggable", false);
llkLowRam = android::base::GetBoolProperty("ro.config.low_ram", false);
- if (!LLK_ENABLE_DEFAULT && debuggable) {
- llkEnable = android::base::GetProperty(LLK_ENABLE_PROPERTY, "eng") == "eng";
- khtEnable = android::base::GetProperty(KHT_ENABLE_PROPERTY, "eng") == "eng";
+ llkEnableSysrqT &= !llkLowRam;
+ if (debuggable) {
+ llkEnableSysrqT |= llkCheckEng(LLK_ENABLE_SYSRQ_T_PROPERTY);
+ if (!LLK_ENABLE_DEFAULT) { // NB: default is currently true ...
+ llkEnable |= llkCheckEng(LLK_ENABLE_PROPERTY);
+ khtEnable |= llkCheckEng(KHT_ENABLE_PROPERTY);
+ }
}
+ llkEnableSysrqT = android::base::GetBoolProperty(LLK_ENABLE_SYSRQ_T_PROPERTY, llkEnableSysrqT);
llkEnable = android::base::GetBoolProperty(LLK_ENABLE_PROPERTY, llkEnable);
if (llkEnable && !llkTopDirectory.reset(procdir)) {
// Most likely reason we could be here is llkd was started
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index e3c4ccc..c9c9e8e 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -1260,6 +1260,8 @@
return maxprocp;
}
+static int last_killed_pid = -1;
+
/* Kill one process specified by procp. Returns the size of the process killed */
static int kill_one_process(struct proc* procp) {
int pid = procp->pid;
@@ -1304,6 +1306,8 @@
TRACE_KILL_END();
+ last_killed_pid = pid;
+
if (r) {
ALOGE("kill(%d): errno=%d", pid, errno);
goto out;
@@ -1331,14 +1335,12 @@
}
/*
- * Find processes to kill to free required number of pages.
- * If pages_to_free is set to 0 only one process will be killed.
- * Returns the size of the killed processes.
+ * Find one process to kill at or above the given oom_adj level.
+ * Returns size of the killed process.
*/
-static int find_and_kill_processes(int min_score_adj, int pages_to_free) {
+static int find_and_kill_process(int min_score_adj) {
int i;
- int killed_size;
- int pages_freed = 0;
+ int killed_size = 0;
#ifdef LMKD_LOG_STATS
bool lmk_state_change_start = false;
@@ -1363,20 +1365,12 @@
LMK_STATE_CHANGE_START);
}
#endif
-
- pages_freed += killed_size;
- if (pages_freed >= pages_to_free) {
-
-#ifdef LMKD_LOG_STATS
- if (enable_stats_log && lmk_state_change_start) {
- stats_write_lmk_state_changed(log_ctx, LMK_STATE_CHANGED,
- LMK_STATE_CHANGE_STOP);
- }
-#endif
- return pages_freed;
- }
+ break;
}
}
+ if (killed_size) {
+ break;
+ }
}
#ifdef LMKD_LOG_STATS
@@ -1385,7 +1379,7 @@
}
#endif
- return pages_freed;
+ return killed_size;
}
static int64_t get_memory_usage(struct reread_data *file_data) {
@@ -1445,6 +1439,23 @@
level - 1 : level);
}
+static bool is_kill_pending(void) {
+ char buf[24];
+
+ if (last_killed_pid < 0) {
+ return false;
+ }
+
+ snprintf(buf, sizeof(buf), "/proc/%d/", last_killed_pid);
+ if (access(buf, F_OK) == 0) {
+ return true;
+ }
+
+ // reset last killed PID because there's nothing pending
+ last_killed_pid = -1;
+ return false;
+}
+
static void mp_event_common(int data, uint32_t events __unused) {
int ret;
unsigned long long evcount;
@@ -1459,7 +1470,6 @@
enum vmpressure_level level = (enum vmpressure_level)data;
long other_free = 0, other_file = 0;
int min_score_adj;
- int pages_to_free = 0;
int minfree = 0;
static struct reread_data mem_usage_file_data = {
.filename = MEMCG_MEMORY_USAGE,
@@ -1490,7 +1500,11 @@
}
if (kill_timeout_ms) {
- if (get_time_diff_ms(&last_kill_tm, &curr_tm) < kill_timeout_ms) {
+ // If we're within the timeout, see if there's pending reclaim work
+ // from the last killed process. If there is (as evidenced by
+ // /proc/<pid> continuing to exist), skip killing for now.
+ if ((get_time_diff_ms(&last_kill_tm, &curr_tm) < kill_timeout_ms) &&
+ (low_ram_device || is_kill_pending())) {
kill_skip_count++;
return;
}
@@ -1537,9 +1551,6 @@
return;
}
- /* Free up enough pages to push over the highest minfree level */
- pages_to_free = lowmem_minfree[lowmem_targets_size - 1] -
- ((other_free < other_file) ? other_free : other_file);
goto do_kill;
}
@@ -1595,7 +1606,7 @@
do_kill:
if (low_ram_device) {
/* For Go devices kill only one task */
- if (find_and_kill_processes(level_oomadj[level], 0) == 0) {
+ if (find_and_kill_process(level_oomadj[level]) == 0) {
if (debug_process_killing) {
ALOGI("Nothing to kill");
}
@@ -1619,10 +1630,7 @@
return;
}
/* Free up enough memory to downgrate the memory pressure to low level */
- if (mi.field.nr_free_pages < low_pressure_mem.max_nr_free_pages) {
- pages_to_free = low_pressure_mem.max_nr_free_pages -
- mi.field.nr_free_pages;
- } else {
+ if (mi.field.nr_free_pages >= low_pressure_mem.max_nr_free_pages) {
if (debug_process_killing) {
ALOGI("Ignoring pressure since more memory is "
"available (%" PRId64 ") than watermark (%" PRId64 ")",
@@ -1633,7 +1641,7 @@
min_score_adj = level_oomadj[level];
}
- pages_freed = find_and_kill_processes(min_score_adj, pages_to_free);
+ pages_freed = find_and_kill_process(min_score_adj);
if (pages_freed == 0) {
/* Rate limit kill reports when nothing was reclaimed */
@@ -1641,25 +1649,24 @@
report_skip_count++;
return;
}
+ } else {
+ /* If we killed anything, update the last killed timestamp. */
+ last_kill_tm = curr_tm;
}
/* Log meminfo whenever we kill or when report rate limit allows */
meminfo_log(&mi);
- if (pages_freed >= pages_to_free) {
- /* Reset kill time only if reclaimed enough memory */
- last_kill_tm = curr_tm;
- }
if (use_minfree_levels) {
- ALOGI("Killing to reclaim %ldkB, reclaimed %ldkB, cache(%ldkB) and "
+ ALOGI("Reclaimed %ldkB, cache(%ldkB) and "
"free(%" PRId64 "kB)-reserved(%" PRId64 "kB) below min(%ldkB) for oom_adj %d",
- pages_to_free * page_k, pages_freed * page_k,
+ pages_freed * page_k,
other_file * page_k, mi.field.nr_free_pages * page_k,
zi.field.totalreserve_pages * page_k,
minfree * page_k, min_score_adj);
} else {
- ALOGI("Killing to reclaim %ldkB, reclaimed %ldkB at oom_adj %d",
- pages_to_free * page_k, pages_freed * page_k, min_score_adj);
+ ALOGI("Reclaimed %ldkB at oom_adj %d",
+ pages_freed * page_k, min_score_adj);
}
if (report_skip_count > 0) {
diff --git a/logcat/Android.mk b/logcat/Android.mk
deleted file mode 100644
index a716993..0000000
--- a/logcat/Android.mk
+++ /dev/null
@@ -1,5 +0,0 @@
-# Copyright 2006-2014 The Android Open Source Project
-
-LOCAL_PATH := $(call my-dir)
-
-include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/logcat/tests/Android.bp b/logcat/tests/Android.bp
new file mode 100644
index 0000000..e1f4d6f
--- /dev/null
+++ b/logcat/tests/Android.bp
@@ -0,0 +1,59 @@
+//
+// Copyright (C) 2013-2014 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.
+//
+
+cc_defaults {
+ name: "logcat-tests-defaults",
+ cflags: [
+ "-fstack-protector-all",
+ "-g",
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-fno-builtin",
+ ],
+}
+
+// -----------------------------------------------------------------------------
+// Benchmarks
+// ----------------------------------------------------------------------------
+
+// Build benchmarks for the device. Run with:
+// adb shell /data/nativetest/logcat-benchmarks/logcat-benchmarks
+cc_benchmark {
+ name: "logcat-benchmarks",
+ defaults: ["logcat-tests-defaults"],
+ srcs: ["logcat_benchmark.cpp"],
+ shared_libs: ["libbase"],
+}
+
+// -----------------------------------------------------------------------------
+// Unit tests.
+// -----------------------------------------------------------------------------
+
+// Build tests for the device (with .so). Run with:
+// adb shell /data/nativetest/logcat-unit-tests/logcat-unit-tests
+cc_test {
+ name: "logcat-unit-tests",
+ defaults: ["logcat-tests-defaults"],
+ shared_libs: [
+ "liblog",
+ "libbase",
+ ],
+ srcs: [
+ "logcat_test.cpp",
+ "logcatd_test.cpp",
+ ],
+}
diff --git a/logcat/tests/Android.mk b/logcat/tests/Android.mk
deleted file mode 100644
index 66f6724..0000000
--- a/logcat/tests/Android.mk
+++ /dev/null
@@ -1,62 +0,0 @@
-#
-# Copyright (C) 2013-2014 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-test_module_prefix := logcat-
-test_tags := tests
-
-test_c_flags := \
- -fstack-protector-all \
- -g \
- -Wall -Wextra \
- -Werror \
- -fno-builtin \
-
-# -----------------------------------------------------------------------------
-# Benchmarks
-# ----------------------------------------------------------------------------
-
-benchmark_src_files := \
- logcat_benchmark.cpp \
-
-# Build benchmarks for the device. Run with:
-# adb shell /data/nativetest/logcat-benchmarks/logcat-benchmarks
-include $(CLEAR_VARS)
-LOCAL_MODULE := $(test_module_prefix)benchmarks
-LOCAL_MODULE_TAGS := $(test_tags)
-LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_SRC_FILES := $(benchmark_src_files)
-LOCAL_SHARED_LIBRARIES := libbase
-include $(BUILD_NATIVE_BENCHMARK)
-
-# -----------------------------------------------------------------------------
-# Unit tests.
-# -----------------------------------------------------------------------------
-
-test_src_files := \
- logcat_test.cpp \
- logcatd_test.cpp \
-
-# Build tests for the device (with .so). Run with:
-# adb shell /data/nativetest/logcat-unit-tests/logcat-unit-tests
-include $(CLEAR_VARS)
-LOCAL_MODULE := $(test_module_prefix)unit-tests
-LOCAL_MODULE_TAGS := $(test_tags)
-LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_SHARED_LIBRARIES := liblog libbase
-LOCAL_SRC_FILES := $(test_src_files)
-include $(BUILD_NATIVE_TEST)
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 9483bb2..d5c40be 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -1404,7 +1404,7 @@
int count = 0;
char buffer[BIG_BUFFER];
-#define logcat_regex_prefix ___STRING(logcat) "_test"
+#define logcat_regex_prefix logcat_executable "_test"
snprintf(buffer, sizeof(buffer),
logcat_executable " --pid %d -d -e " logcat_regex_prefix "_a+b",
@@ -1550,7 +1550,7 @@
{
static const struct tag sync = { 2720, "sync" };
- static const char id[] = ___STRING(logcat) ".descriptive-sync";
+ static const char id[] = logcat_executable ".descriptive-sync";
{
android_log_event_list ctx(sync.tagNo);
ctx << id << (int32_t)42 << (int32_t)-1 << (int32_t)0;
@@ -1665,7 +1665,7 @@
// Invent new entries because existing can not serve
EventTagMap* map = android_openEventTagMap(nullptr);
ASSERT_TRUE(nullptr != map);
- static const char name[] = ___STRING(logcat) ".descriptive-monotonic";
+ static const char name[] = logcat_executable ".descriptive-monotonic";
int myTag = android_lookupEventTagNum(map, name, "(new|1|s)",
ANDROID_LOG_UNKNOWN);
android_closeEventTagMap(map);
diff --git a/logd/Android.mk b/logd/Android.mk
index 1bca891..b3ce560 100644
--- a/logd/Android.mk
+++ b/logd/Android.mk
@@ -9,5 +9,3 @@
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/init
include $(BUILD_PREBUILT)
-
-include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp
index 1715501..208d67f 100644
--- a/logd/LogTimes.cpp
+++ b/logd/LogTimes.cpp
@@ -204,6 +204,10 @@
goto skip;
}
+ if (me->mRelease) {
+ goto stop;
+ }
+
if (!me->mTail) {
goto ok;
}
diff --git a/logd/tests/Android.bp b/logd/tests/Android.bp
new file mode 100644
index 0000000..f15beb2
--- /dev/null
+++ b/logd/tests/Android.bp
@@ -0,0 +1,68 @@
+//
+// Copyright (C) 2014 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.
+//
+
+// -----------------------------------------------------------------------------
+// Unit tests.
+// -----------------------------------------------------------------------------
+
+cc_defaults {
+ name: "logd-unit-test-defaults",
+
+ cflags: [
+ "-fstack-protector-all",
+ "-g",
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-fno-builtin",
+
+ "-DAUDITD_LOG_TAG=1003",
+ "-DCHATTY_LOG_TAG=1004",
+ ],
+
+ srcs: ["logd_test.cpp"],
+
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "liblog",
+ "libselinux",
+ ],
+}
+
+// Build tests for the logger. Run with:
+// adb shell /data/nativetest/logd-unit-tests/logd-unit-tests
+cc_test {
+ name: "logd-unit-tests",
+ defaults: ["logd-unit-test-defaults"],
+}
+
+cc_test {
+ name: "CtsLogdTestCases",
+ defaults: ["logd-unit-test-defaults"],
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
+ test_suites: [
+ "cts",
+ "vts",
+ ],
+}
diff --git a/logd/tests/Android.mk b/logd/tests/Android.mk
deleted file mode 100644
index a0875ea..0000000
--- a/logd/tests/Android.mk
+++ /dev/null
@@ -1,86 +0,0 @@
-#
-# Copyright (C) 2014 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-# -----------------------------------------------------------------------------
-# Benchmarks. (see ../../liblog/tests)
-# -----------------------------------------------------------------------------
-
-test_module_prefix := logd-
-test_tags := tests
-
-# -----------------------------------------------------------------------------
-# Unit tests.
-# -----------------------------------------------------------------------------
-
-event_flag := -DAUDITD_LOG_TAG=1003 -DCHATTY_LOG_TAG=1004
-
-test_c_flags := \
- -fstack-protector-all \
- -g \
- -Wall -Wextra \
- -Werror \
- -fno-builtin \
- $(event_flag)
-
-test_src_files := \
- logd_test.cpp
-
-# Build tests for the logger. Run with:
-# adb shell /data/nativetest/logd-unit-tests/logd-unit-tests
-include $(CLEAR_VARS)
-LOCAL_MODULE := $(test_module_prefix)unit-tests
-LOCAL_MODULE_TAGS := $(test_tags)
-LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_SHARED_LIBRARIES := libbase libcutils liblog libselinux
-LOCAL_SRC_FILES := $(test_src_files)
-include $(BUILD_NATIVE_TEST)
-
-cts_executable := CtsLogdTestCases
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := $(cts_executable)
-LOCAL_MODULE_TAGS := tests
-LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_SRC_FILES := $(test_src_files)
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/nativetest
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-LOCAL_SHARED_LIBRARIES := libbase libcutils liblog libselinux
-LOCAL_STATIC_LIBRARIES := libgtest libgtest_main
-LOCAL_COMPATIBILITY_SUITE := cts vts
-LOCAL_CTS_TEST_PACKAGE := android.core.logd
-include $(BUILD_CTS_EXECUTABLE)
-
-ifeq ($(HOST_OS)-$(HOST_ARCH),$(filter $(HOST_OS)-$(HOST_ARCH),linux-x86 linux-x86_64))
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := $(cts_executable)_list
-LOCAL_MODULE_TAGS := optional
-LOCAL_CFLAGS := $(test_c_flags) -DHOST
-LOCAL_C_INCLUDES := external/gtest/include
-LOCAL_SRC_FILES := $(test_src_files)
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-LOCAL_CXX_STL := libc++
-LOCAL_SHARED_LIBRARIES := libbase libcutils liblog
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-include $(BUILD_HOST_NATIVE_TEST)
-
-endif # ifeq ($(HOST_OS)-$(HOST_ARCH),$(filter $(HOST_OS)-$(HOST_ARCH),linux-x86 linux-x86_64))
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 025e3c3..349168e 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -269,6 +269,12 @@
# that they can be chown'd to system:system later on boot
write /sys/class/leds/vibrator/trigger "transient"
+ # This is used by Bionic to select optimized routines.
+ write /dev/cpu_variant:${ro.bionic.arch} ${ro.bionic.cpu_variant}
+ chmod 0444 /dev/cpu_variant:${ro.bionic.arch}
+ write /dev/cpu_variant:${ro.bionic.2nd_arch} ${ro.bionic.2nd_cpu_variant}
+ chmod 0444 /dev/cpu_variant:${ro.bionic.2nd_arch}
+
# Setup APEX mount point and its security context
mount tmpfs tmpfs /apex nodev noexec nosuid
chmod 0755 /apex
@@ -801,3 +807,6 @@
service flash_recovery /system/bin/install-recovery.sh
class main
oneshot
+
+on property:apexd.status=ready
+ parse_apex_configs
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index 2f85dec..d47506c 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -72,7 +72,7 @@
/dev/diag_arm9 0660 radio radio
/dev/ttyMSM0 0600 bluetooth bluetooth
/dev/uhid 0660 uhid uhid
-/dev/uinput 0660 system bluetooth
+/dev/uinput 0660 uhid uhid
/dev/alarm 0664 system radio
/dev/rtc0 0640 system system
/dev/tty0 0660 root system
diff --git a/run-as/.clang-format b/run-as/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/run-as/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/run-as/run-as.cpp b/run-as/run-as.cpp
index d005ecf..f49bdf7 100644
--- a/run-as/run-as.cpp
+++ b/run-as/run-as.cpp
@@ -25,6 +25,8 @@
#include <sys/types.h>
#include <unistd.h>
+#include <string>
+
#include <libminijail.h>
#include <scoped_minijail.h>
@@ -144,7 +146,7 @@
// Some devices can disable running run-as, such as Chrome OS when running in
// non-developer mode.
if (android::base::GetBoolProperty("ro.boot.disable_runas", false)) {
- error(1, 0, "run-as is disabled from the kernel commandline");
+ error(1, 0, "run-as is disabled from the kernel commandline");
}
char* pkgname = argv[1];
@@ -167,6 +169,15 @@
if (!packagelist_parse(packagelist_parse_callback, &info)) {
error(1, errno, "packagelist_parse failed");
}
+
+ // Handle a multi-user data path
+ if (userId > 0) {
+ free(info.data_dir);
+ if (asprintf(&info.data_dir, "/data/user/%d/%s", userId, pkgname) == -1) {
+ error(1, errno, "asprintf failed");
+ }
+ }
+
if (info.uid == 0) {
error(1, 0, "unknown package: %s", pkgname);
}
@@ -205,7 +216,8 @@
minijail_keep_supplementary_gids(j.get());
minijail_enter(j.get());
- if (selinux_android_setcontext(uid, 0, info.seinfo, pkgname) < 0) {
+ std::string seinfo = std::string(info.seinfo) + ":fromRunAs";
+ if (selinux_android_setcontext(uid, 0, seinfo.c_str(), pkgname) < 0) {
error(1, errno, "couldn't set SELinux security context");
}
diff --git a/storaged/storaged_info.cpp b/storaged/storaged_info.cpp
index 8c0b3d1..ca2421b 100644
--- a/storaged/storaged_info.cpp
+++ b/storaged/storaged_info.cpp
@@ -87,12 +87,21 @@
day_start_tp += chrono::seconds(perf_history.day_start_sec());
nr_samples = perf_history.nr_samples();
+ if (nr_samples < recent_perf.size()) {
+ recent_perf.erase(recent_perf.begin() + nr_samples, recent_perf.end());
+ }
+ size_t i = 0;
for (auto bw : perf_history.recent_perf()) {
- recent_perf.push_back(bw);
+ if (i < recent_perf.size()) {
+ recent_perf[i] = bw;
+ } else {
+ recent_perf.push_back(bw);
+ }
+ ++i;
}
nr_days = perf_history.nr_days();
- int i = 0;
+ i = 0;
for (auto bw : perf_history.daily_perf()) {
daily_perf[i++] = bw;
}