Merge "Enable clang-tidy for libsysutils."
diff --git a/adb/Android.bp b/adb/Android.bp
index 01e00dd..87e4adc 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -59,6 +59,9 @@
// MinGW hides some things behind _POSIX_SOURCE.
"-D_POSIX_SOURCE",
+ // libusb uses __stdcall on a variadic function, which gets ignored.
+ "-Wno-ignored-attributes",
+
// Not supported yet.
"-Wno-thread-safety",
],
@@ -506,6 +509,52 @@
}
cc_binary {
+ name: "static_adbd",
+ defaults: ["adbd_defaults", "host_adbd_supported"],
+
+ recovery_available: false,
+ static_executable: true,
+ host_supported: false,
+
+ srcs: [
+ "daemon/main.cpp",
+ ],
+
+ cflags: [
+ "-D_GNU_SOURCE",
+ "-Wno-deprecated-declarations",
+ ],
+
+ strip: {
+ keep_symbols: true,
+ },
+
+ static_libs: [
+ "libadbd",
+ "libadbd_services",
+ "libasyncio",
+ "libavb_user",
+ "libbase",
+ "libbootloader_message",
+ "libcap",
+ "libcrypto",
+ "libcrypto_utils",
+ "libcutils",
+ "libdiagnose_usb",
+ "libext4_utils",
+ "libfec",
+ "libfec_rs",
+ "libfs_mgr",
+ "liblog",
+ "liblp",
+ "libmdnssd",
+ "libminijail",
+ "libselinux",
+ "libsquashfs_utils",
+ ],
+}
+
+cc_binary {
name: "abb",
defaults: ["adbd_defaults"],
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 2dd22b3..24d4292 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -1048,9 +1048,9 @@
// New transport selection protocol:
// This is essentially identical to the previous version, except it returns the selected
// transport id to the caller as well.
- if (ConsumePrefix(&service, "tport:")) {
+ if (android::base::ConsumePrefix(&service, "tport:")) {
legacy = false;
- if (ConsumePrefix(&service, "serial:")) {
+ if (android::base::ConsumePrefix(&service, "serial:")) {
serial_storage = service;
serial = serial_storage.c_str();
} else if (service == "usb") {
@@ -1064,7 +1064,7 @@
// Selection by id is unimplemented, since you obviously already know the transport id
// you're connecting to.
} else {
- if (ConsumePrefix(&service, "transport-id:")) {
+ if (android::base::ConsumePrefix(&service, "transport-id:")) {
if (!ParseUint(&transport_id, service)) {
SendFail(reply_fd, "invalid transport id");
return HostRequestResult::Handled;
@@ -1075,7 +1075,7 @@
type = kTransportLocal;
} else if (service == "transport-any") {
type = kTransportAny;
- } else if (ConsumePrefix(&service, "transport:")) {
+ } else if (android::base::ConsumePrefix(&service, "transport:")) {
serial_storage = service;
serial = serial_storage.c_str();
}
@@ -1216,7 +1216,7 @@
}
// Indicates a new emulator instance has started.
- if (ConsumePrefix(&service, "emulator:")) {
+ if (android::base::ConsumePrefix(&service, "emulator:")) {
unsigned int port;
if (!ParseUint(&port, service)) {
LOG(ERROR) << "received invalid port for emulator: " << service;
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index 5800a62..8b0dcee 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -141,11 +141,3 @@
return true;
}
-
-inline bool ConsumePrefix(std::string_view* str, std::string_view prefix) {
- if (str->starts_with(prefix)) {
- str->remove_prefix(prefix.size());
- return true;
- }
- return false;
-}
diff --git a/adb/client/auth.cpp b/adb/client/auth.cpp
index 3eee426..ed6a9a8 100644
--- a/adb/client/auth.cpp
+++ b/adb/client/auth.cpp
@@ -53,6 +53,25 @@
*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() {
+ 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)
+ if (username.empty() && getlogin()) username = getlogin();
+#endif
+ if (username.empty()) hostname = "unknown";
+
+ return " " + username + "@" + hostname;
+}
+
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))) {
@@ -70,6 +89,7 @@
size_t actual_length = EVP_EncodeBlock(reinterpret_cast<uint8_t*>(out->data()), binary_key_data,
sizeof(binary_key_data));
out->resize(actual_length);
+ out->append(get_user_info());
return true;
}
@@ -79,6 +99,7 @@
mode_t old_mask;
FILE *f = nullptr;
int ret = 0;
+ std::string pubkey;
EVP_PKEY* pkey = EVP_PKEY_new();
BIGNUM* exponent = BN_new();
@@ -92,6 +113,11 @@
RSA_generate_key_ex(rsa, 2048, exponent, nullptr);
EVP_PKEY_set1_RSA(pkey, rsa);
+ if (!calculate_public_key(&pubkey, rsa)) {
+ LOG(ERROR) << "failed to calculate public key";
+ goto out;
+ }
+
old_mask = umask(077);
f = fopen(file.c_str(), "w");
@@ -104,7 +130,12 @@
umask(old_mask);
if (!PEM_write_PrivateKey(f, pkey, nullptr, nullptr, 0, nullptr, nullptr)) {
- D("Failed to write key");
+ LOG(ERROR) << "Failed to write key";
+ goto out;
+ }
+
+ if (!android::base::WriteStringToFile(pubkey, file + ".pub")) {
+ PLOG(ERROR) << "failed to write public key";
goto out;
}
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 552df41..f39aa58 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -904,12 +904,12 @@
}
buf[8] = '\0';
- if (strcmp(kSideloadServiceExitSuccess, buf) == 0 ||
- strcmp(kSideloadServiceExitFailure, buf) == 0) {
+ if (strcmp(kMinadbdServicesExitSuccess, buf) == 0 ||
+ strcmp(kMinadbdServicesExitFailure, buf) == 0) {
printf("\rTotal xfer: %.2fx%*s\n",
static_cast<double>(xfer) / (sb.st_size ? sb.st_size : 1),
static_cast<int>(strlen(filename) + 10), "");
- if (strcmp(kSideloadServiceExitFailure, buf) == 0) {
+ if (strcmp(kMinadbdServicesExitFailure, buf) == 0) {
return 1;
}
return 0;
@@ -961,6 +961,33 @@
}
}
+static int adb_wipe_devices() {
+ auto wipe_devices_message_size = strlen(kMinadbdServicesExitSuccess);
+ std::string error;
+ unique_fd fd(adb_connect(
+ android::base::StringPrintf("rescue-wipe:userdata:%zu", wipe_devices_message_size),
+ &error));
+ if (fd < 0) {
+ fprintf(stderr, "adb: wipe device connection failed: %s\n", error.c_str());
+ return 1;
+ }
+
+ std::string message(wipe_devices_message_size, '\0');
+ if (!ReadFdExactly(fd, message.data(), wipe_devices_message_size)) {
+ fprintf(stderr, "adb: failed to read wipe result: %s\n", strerror(errno));
+ return 1;
+ }
+
+ if (message == kMinadbdServicesExitSuccess) {
+ return 0;
+ }
+
+ if (message != kMinadbdServicesExitFailure) {
+ fprintf(stderr, "adb: got unexpected message from rescue wipe %s\n", message.c_str());
+ }
+ return 1;
+}
+
/**
* Run ppp in "notty" mode against a resource listed as the first parameter
* eg:
@@ -1115,7 +1142,7 @@
// If we were using a specific transport ID, there's nothing we can wait for.
if (previous_id == 0) {
adb_set_transport(previous_type, previous_serial, 0);
- wait_for_device("wait-for-device", 3000ms);
+ wait_for_device("wait-for-device", 6000ms);
}
return true;
@@ -1643,6 +1670,7 @@
} else if (!strcmp(argv[0], "rescue")) {
// adb rescue getprop <prop>
// adb rescue install <filename>
+ // adb rescue wipe userdata
if (argc != 3) error_exit("rescue requires two arguments");
if (!strcmp(argv[1], "getprop")) {
return adb_connect_command(android::base::StringPrintf("rescue-getprop:%s", argv[2]));
@@ -1650,6 +1678,8 @@
if (adb_sideload_install(argv[2], true /* rescue_mode */) != 0) {
return 1;
}
+ } else if (!strcmp(argv[1], "wipe") && !strcmp(argv[2], "userdata")) {
+ return adb_wipe_devices();
} else {
error_exit("invalid rescue argument");
}
diff --git a/adb/daemon/abb.cpp b/adb/daemon/abb.cpp
index eeac41a..aa75bb1 100644
--- a/adb/daemon/abb.cpp
+++ b/adb/daemon/abb.cpp
@@ -17,6 +17,7 @@
#include <sys/wait.h>
#include <android-base/cmsg.h>
+#include <android-base/strings.h>
#include <cmd.h>
#include "adb.h"
@@ -87,9 +88,9 @@
std::string_view name = data;
auto protocol = SubprocessProtocol::kShell;
- if (ConsumePrefix(&name, "abb:")) {
+ if (android::base::ConsumePrefix(&name, "abb:")) {
protocol = SubprocessProtocol::kShell;
- } else if (ConsumePrefix(&name, "abb_exec:")) {
+ } else if (android::base::ConsumePrefix(&name, "abb_exec:")) {
protocol = SubprocessProtocol::kNone;
} else {
LOG(FATAL) << "Unknown command prefix for abb: " << data;
diff --git a/adb/daemon/file_sync_service.cpp b/adb/daemon/file_sync_service.cpp
index e82a51f..0e70d47 100644
--- a/adb/daemon/file_sync_service.cpp
+++ b/adb/daemon/file_sync_service.cpp
@@ -229,17 +229,13 @@
static bool handle_send_file(int s, const char* path, uint32_t* timestamp, uid_t uid, gid_t gid,
uint64_t capabilities, mode_t mode, std::vector<char>& buffer,
bool do_unlink) {
+ int rc;
syncmsg msg;
__android_log_security_bswrite(SEC_TAG_ADB_SEND_FILE, path);
unique_fd fd(adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode));
- if (posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE | POSIX_FADV_WILLNEED) <
- 0) {
- D("[ Failed to fadvise: %d ]", errno);
- }
-
if (fd < 0 && errno == ENOENT) {
if (!secure_mkdirs(Dirname(path))) {
SendSyncFailErrno(s, "secure_mkdirs failed");
@@ -270,6 +266,12 @@
fchmod(fd.get(), mode);
}
+ rc = posix_fadvise(fd.get(), 0, 0,
+ POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE | POSIX_FADV_WILLNEED);
+ if (rc != 0) {
+ D("[ Failed to fadvise: %s ]", strerror(rc));
+ }
+
while (true) {
if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) goto fail;
@@ -464,8 +466,9 @@
return false;
}
- if (posix_fadvise(fd.get(), 0, 0, POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE) < 0) {
- D("[ Failed to fadvise: %d ]", errno);
+ int rc = posix_fadvise(fd.get(), 0, 0, POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE);
+ if (rc != 0) {
+ D("[ Failed to fadvise: %s ]", strerror(rc));
}
syncmsg msg;
diff --git a/adb/daemon/services.cpp b/adb/daemon/services.cpp
index b0cc450..e6f4499 100644
--- a/adb/daemon/services.cpp
+++ b/adb/daemon/services.cpp
@@ -223,13 +223,13 @@
return create_jdwp_service_socket();
} else if (name == "track-jdwp") {
return create_jdwp_tracker_service_socket();
- } else if (ConsumePrefix(&name, "sink:")) {
+ } else if (android::base::ConsumePrefix(&name, "sink:")) {
uint64_t byte_count = 0;
if (!ParseUint(&byte_count, name)) {
return nullptr;
}
return new SinkSocket(byte_count);
- } else if (ConsumePrefix(&name, "source:")) {
+ } else if (android::base::ConsumePrefix(&name, "source:")) {
uint64_t byte_count = 0;
if (!ParseUint(&byte_count, name)) {
return nullptr;
@@ -250,11 +250,11 @@
#if defined(__ANDROID__)
if (name.starts_with("framebuffer:")) {
return create_service_thread("fb", framebuffer_service);
- } else if (ConsumePrefix(&name, "remount:")) {
+ } else if (android::base::ConsumePrefix(&name, "remount:")) {
std::string arg(name);
return create_service_thread("remount",
std::bind(remount_service, std::placeholders::_1, arg));
- } else if (ConsumePrefix(&name, "reboot:")) {
+ } else if (android::base::ConsumePrefix(&name, "reboot:")) {
std::string arg(name);
return create_service_thread("reboot",
std::bind(reboot_service, std::placeholders::_1, arg));
@@ -262,7 +262,7 @@
return create_service_thread("root", restart_root_service);
} else if (name.starts_with("unroot:")) {
return create_service_thread("unroot", restart_unroot_service);
- } else if (ConsumePrefix(&name, "backup:")) {
+ } else if (android::base::ConsumePrefix(&name, "backup:")) {
std::string cmd = "/system/bin/bu backup ";
cmd += name;
return StartSubprocess(cmd, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
@@ -275,7 +275,7 @@
} else if (name.starts_with("enable-verity:")) {
return create_service_thread("verity-off", std::bind(set_verity_enabled_state_service,
std::placeholders::_1, true));
- } else if (ConsumePrefix(&name, "tcpip:")) {
+ } else if (android::base::ConsumePrefix(&name, "tcpip:")) {
std::string str(name);
int port;
@@ -289,22 +289,22 @@
}
#endif
- if (ConsumePrefix(&name, "dev:")) {
+ if (android::base::ConsumePrefix(&name, "dev:")) {
return unique_fd{unix_open(name, O_RDWR | O_CLOEXEC)};
- } else if (ConsumePrefix(&name, "jdwp:")) {
+ } else if (android::base::ConsumePrefix(&name, "jdwp:")) {
pid_t pid;
if (!ParseUint(&pid, name)) {
return unique_fd{};
}
return create_jdwp_connection_fd(pid);
- } else if (ConsumePrefix(&name, "shell")) {
+ } else if (android::base::ConsumePrefix(&name, "shell")) {
return ShellService(name, transport);
- } else if (ConsumePrefix(&name, "exec:")) {
+ } else if (android::base::ConsumePrefix(&name, "exec:")) {
return StartSubprocess(std::string(name), nullptr, SubprocessType::kRaw,
SubprocessProtocol::kNone);
} else if (name.starts_with("sync:")) {
return create_service_thread("sync", file_sync_service);
- } else if (ConsumePrefix(&name, "reverse:")) {
+ } else if (android::base::ConsumePrefix(&name, "reverse:")) {
return reverse_service(name, transport);
} else if (name == "reconnect") {
return create_service_thread(
diff --git a/adb/daemon/set_verity_enable_state_service.cpp b/adb/daemon/set_verity_enable_state_service.cpp
index 889229f..49dce66 100644
--- a/adb/daemon/set_verity_enable_state_service.cpp
+++ b/adb/daemon/set_verity_enable_state_service.cpp
@@ -111,8 +111,11 @@
WriteFdFmt(fd, "%s overlayfs for %s\n", enable ? "disabling" : "using", mount_point);
}
} else if (errno) {
- WriteFdFmt(fd, "Overlayfs %s for %s failed with error %s\n", enable ? "teardown" : "setup",
- mount_point, strerror(errno));
+ int expected_errno = enable ? EBUSY : ENOENT;
+ if (errno != expected_errno) {
+ WriteFdFmt(fd, "Overlayfs %s for %s failed with error %s\n",
+ enable ? "teardown" : "setup", mount_point, strerror(errno));
+ }
}
WriteFdFmt(fd, "Verity %s on %s\n", enable ? "enabled" : "disabled", mount_point);
return true;
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index 3b29ab5..0a116ab 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -58,10 +58,12 @@
static std::optional<bool> gFfsAioSupported;
// Not all USB controllers support operations larger than 16k, so don't go above that.
-static constexpr size_t kUsbReadQueueDepth = 32;
+// Also, each submitted operation does an allocation in the kernel of that size, so we want to
+// minimize our queue depth while still maintaining a deep enough queue to keep the USB stack fed.
+static constexpr size_t kUsbReadQueueDepth = 8;
static constexpr size_t kUsbReadSize = 4 * PAGE_SIZE;
-static constexpr size_t kUsbWriteQueueDepth = 32;
+static constexpr size_t kUsbWriteQueueDepth = 8;
static constexpr size_t kUsbWriteSize = 4 * PAGE_SIZE;
static const char* to_string(enum usb_functionfs_event_type type) {
@@ -308,11 +310,13 @@
if (bound) {
LOG(WARNING) << "received FUNCTIONFS_BIND while already bound?";
running = false;
+ break;
}
if (enabled) {
LOG(WARNING) << "received FUNCTIONFS_BIND while already enabled?";
running = false;
+ break;
}
bound = true;
@@ -322,11 +326,13 @@
if (!bound) {
LOG(WARNING) << "received FUNCTIONFS_ENABLE while not bound?";
running = false;
+ break;
}
if (enabled) {
LOG(WARNING) << "received FUNCTIONFS_ENABLE while already enabled?";
running = false;
+ break;
}
enabled = true;
diff --git a/adb/services.cpp b/adb/services.cpp
index 335ffc4..6185aa6 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -100,7 +100,7 @@
ConnectionState state;
};
-static void wait_for_state(int fd, state_info* sinfo) {
+static void wait_for_state(unique_fd fd, state_info* sinfo) {
D("wait_for_state %d", sinfo->state);
while (true) {
@@ -122,7 +122,7 @@
}
if (!is_ambiguous) {
- adb_pollfd pfd = {.fd = fd, .events = POLLIN};
+ adb_pollfd pfd = {.fd = fd.get(), .events = POLLIN};
int rc = adb_poll(&pfd, 1, 100);
if (rc < 0) {
SendFail(fd, error);
@@ -140,7 +140,6 @@
}
}
- adb_close(fd);
D("wait_for_state is done");
}
@@ -203,7 +202,7 @@
return create_device_tracker(false);
} else if (name == "track-devices-l") {
return create_device_tracker(true);
- } else if (ConsumePrefix(&name, "wait-for-")) {
+ } else if (android::base::ConsumePrefix(&name, "wait-for-")) {
std::shared_ptr<state_info> sinfo = std::make_shared<state_info>();
if (sinfo == nullptr) {
fprintf(stderr, "couldn't allocate state_info: %s", strerror(errno));
@@ -213,11 +212,11 @@
sinfo->serial = serial;
sinfo->transport_id = transport_id;
- if (ConsumePrefix(&name, "local")) {
+ if (android::base::ConsumePrefix(&name, "local")) {
sinfo->transport_type = kTransportLocal;
- } else if (ConsumePrefix(&name, "usb")) {
+ } else if (android::base::ConsumePrefix(&name, "usb")) {
sinfo->transport_type = kTransportUsb;
- } else if (ConsumePrefix(&name, "any")) {
+ } else if (android::base::ConsumePrefix(&name, "any")) {
sinfo->transport_type = kTransportAny;
} else {
return nullptr;
@@ -241,11 +240,10 @@
return nullptr;
}
- unique_fd fd = create_service_thread("wait", [sinfo](int fd) {
- wait_for_state(fd, sinfo.get());
- });
+ unique_fd fd = create_service_thread(
+ "wait", [sinfo](unique_fd fd) { wait_for_state(std::move(fd), sinfo.get()); });
return create_local_socket(std::move(fd));
- } else if (ConsumePrefix(&name, "connect:")) {
+ } else if (android::base::ConsumePrefix(&name, "connect:")) {
std::string host(name);
unique_fd fd = create_service_thread(
"connect", std::bind(connect_service, std::placeholders::_1, host));
diff --git a/adb/services.h b/adb/services.h
index 8f3919b..6fc89d7 100644
--- a/adb/services.h
+++ b/adb/services.h
@@ -23,9 +23,10 @@
constexpr char kShellServiceArgPty[] = "pty";
constexpr char kShellServiceArgShellProtocol[] = "v2";
-// Special flags sent by minadbd that indicate the end of sideload transfer and install result.
-constexpr char kSideloadServiceExitSuccess[] = "DONEDONE";
-constexpr char kSideloadServiceExitFailure[] = "FAILFAIL";
+// Special flags sent by minadbd. They indicate the end of sideload transfer and the result of
+// installation or wipe.
+constexpr char kMinadbdServicesExitSuccess[] = "DONEDONE";
+constexpr char kMinadbdServicesExitFailure[] = "FAILFAIL";
unique_fd create_service_thread(const char* service_name, std::function<void(unique_fd)> func);
#endif // SERVICES_H_
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 8a2bf9a..75993b3 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -31,6 +31,8 @@
#include <string>
#include <vector>
+#include <android-base/strings.h>
+
#if !ADB_HOST
#include <android-base/properties.h>
#include <log/log_properties.h>
@@ -755,26 +757,26 @@
#if ADB_HOST
service = std::string_view(s->smart_socket_data).substr(4);
- if (ConsumePrefix(&service, "host-serial:")) {
+ if (android::base::ConsumePrefix(&service, "host-serial:")) {
// serial number should follow "host:" and could be a host:port string.
if (!internal::parse_host_service(&serial, &service, service)) {
LOG(ERROR) << "SS(" << s->id << "): failed to parse host service: " << service;
goto fail;
}
- } else if (ConsumePrefix(&service, "host-transport-id:")) {
+ } else if (android::base::ConsumePrefix(&service, "host-transport-id:")) {
if (!ParseUint(&transport_id, service, &service)) {
LOG(ERROR) << "SS(" << s->id << "): failed to parse host transport id: " << service;
return -1;
}
- if (!ConsumePrefix(&service, ":")) {
+ if (!android::base::ConsumePrefix(&service, ":")) {
LOG(ERROR) << "SS(" << s->id << "): host-transport-id without command";
return -1;
}
- } else if (ConsumePrefix(&service, "host-usb:")) {
+ } else if (android::base::ConsumePrefix(&service, "host-usb:")) {
type = kTransportUsb;
- } else if (ConsumePrefix(&service, "host-local:")) {
+ } else if (android::base::ConsumePrefix(&service, "host-local:")) {
type = kTransportLocal;
- } else if (ConsumePrefix(&service, "host:")) {
+ } else if (android::base::ConsumePrefix(&service, "host:")) {
type = kTransportAny;
} else {
service = std::string_view{};
diff --git a/base/include/android-base/strings.h b/base/include/android-base/strings.h
index 8e9716f..b1c22c9 100644
--- a/base/include/android-base/strings.h
+++ b/base/include/android-base/strings.h
@@ -18,6 +18,7 @@
#include <sstream>
#include <string>
+#include <string_view>
#include <vector>
namespace android {
@@ -68,5 +69,21 @@
// Tests whether 'lhs' equals 'rhs', ignoring case.
bool EqualsIgnoreCase(std::string_view lhs, std::string_view rhs);
+// Removes `prefix` from the start of the given string and returns true (if
+// it was present), false otherwise.
+inline bool ConsumePrefix(std::string_view* s, std::string_view prefix) {
+ if (!StartsWith(*s, prefix)) return false;
+ s->remove_prefix(prefix.size());
+ return true;
+}
+
+// Removes `suffix` from the end of the given string and returns true (if
+// it was present), false otherwise.
+inline bool ConsumeSuffix(std::string_view* s, std::string_view suffix) {
+ if (!EndsWith(*s, suffix)) return false;
+ s->remove_suffix(suffix.size());
+ return true;
+}
+
} // namespace base
} // namespace android
diff --git a/base/strings_test.cpp b/base/strings_test.cpp
index 9d74094..ca3c0b8 100644
--- a/base/strings_test.cpp
+++ b/base/strings_test.cpp
@@ -295,3 +295,19 @@
TEST(strings, ubsan_28729303) {
android::base::Split("/dev/null", ":");
}
+
+TEST(strings, ConsumePrefix) {
+ std::string_view s{"foo.bar"};
+ ASSERT_FALSE(android::base::ConsumePrefix(&s, "bar."));
+ ASSERT_EQ("foo.bar", s);
+ ASSERT_TRUE(android::base::ConsumePrefix(&s, "foo."));
+ ASSERT_EQ("bar", s);
+}
+
+TEST(strings, ConsumeSuffix) {
+ std::string_view s{"foo.bar"};
+ ASSERT_FALSE(android::base::ConsumeSuffix(&s, ".foo"));
+ ASSERT_EQ("foo.bar", s);
+ ASSERT_TRUE(android::base::ConsumeSuffix(&s, ".bar"));
+ ASSERT_EQ("foo", s);
+}
diff --git a/bootstat/boot_reason_test.sh b/bootstat/boot_reason_test.sh
index 71d3ecb..8979b0c 100755
--- a/bootstat/boot_reason_test.sh
+++ b/bootstat/boot_reason_test.sh
@@ -25,6 +25,8 @@
# Best guess to an average device's reboot time, refined as tests return
DURATION_DEFAULT=45
STOP_ON_FAILURE=false
+progname="${0##*/}"
+progpath="${0%${progname}}"
# Helper functions
@@ -42,11 +44,40 @@
adb devices | grep -v 'List of devices attached' | grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
}
+[ "USAGE: adb_sh <commands> </dev/stdin >/dev/stdout 2>/dev/stderr
+
+Returns: true if the command succeeded" ]
+adb_sh() {
+ local args=
+ for i in "${@}"; do
+ [ -z "${args}" ] || args="${args} "
+ if [ X"${i}" != X"${i#\'}" ]; then
+ args="${args}${i}"
+ elif [ X"${i}" != X"${i#*\\}" ]; then
+ args="${args}`echo ${i} | sed 's/\\\\/\\\\\\\\/g'`"
+ elif [ X"${i}" != X"${i#* }" ]; then
+ args="${args}'${i}'"
+ elif [ X"${i}" != X"${i#*${TAB}}" ]; then
+ args="${args}'${i}'"
+ else
+ args="${args}${i}"
+ fi
+ done
+ adb shell "${args}"
+}
+
+[ "USAGE: adb_su <commands> </dev/stdin >/dev/stdout 2>/dev/stderr
+
+Returns: true if the command running as root succeeded" ]
+adb_su() {
+ adb_sh su root "${@}"
+}
+
[ "USAGE: hasPstore
Returns: true if device (likely) has pstore data" ]
hasPstore() {
- if inAdb && [ 0 -eq `adb shell su root ls /sys/fs/pstore | wc -l` ]; then
+ if inAdb && [ 0 -eq `adb_su ls /sys/fs/pstore </dev/null | wc -l` ]; then
false
fi
}
@@ -55,7 +86,7 @@
Returns the property value" ]
get_property() {
- adb shell getprop ${1} 2>&1 </dev/null
+ adb_sh getprop ${1} 2>&1 </dev/null
}
[ "USAGE: isDebuggable
@@ -89,18 +120,18 @@
Returns: true if device supports and set boot reason injection" ]
setBootloaderBootReason() {
inAdb || ( echo "ERROR: device not in adb mode." >&2 ; false ) || return 1
- if [ -z "`adb shell ls /etc/init/bootstat-debug.rc 2>/dev/null`" ]; then
+ if [ -z "`adb_sh ls /etc/init/bootstat-debug.rc 2>/dev/null </dev/null`" ]; then
echo "ERROR: '${TEST}' test requires /etc/init/bootstat-debug.rc" >&2
return 1
fi
checkDebugBuild || return 1
- if adb shell su root "cat /proc/cmdline | tr '\\0 ' '\\n\\n'" |
+ if adb_su "cat /proc/cmdline | tr '\\0 ' '\\n\\n'" </dev/null |
grep '^androidboot[.]bootreason=[^ ]' >/dev/null; then
echo "ERROR: '${TEST}' test requires a device with a bootloader that" >&2
echo " does not set androidboot.bootreason kernel parameter." >&2
return 1
fi
- adb shell su root setprop persist.test.boot.reason "'${1}'" 2>/dev/null
+ adb_su setprop persist.test.boot.reason "'${1}'" 2>/dev/null </dev/null
test_reason="`get_property persist.test.boot.reason`"
if [ X"${test_reason}" != X"${1}" ]; then
echo "ERROR: can not set persist.test.boot.reason to '${1}'." >&2
@@ -299,7 +330,14 @@
return ${save_ret}
}
-[ "USAGE: report_bootstat_logs <expected> ...
+[ "USAGE: adb_date >/dev/stdout
+
+Returns: report device epoch time (suitable for logcat -t)" ]
+adb_date() {
+ adb_sh date +%s.%N </dev/null
+}
+
+[ "USAGE: report_bootstat_logs [-t<timestamp>] <expected> ...
if not prefixed with a minus (-), <expected> will become a series of expected
matches:
@@ -314,8 +352,11 @@
report_bootstat_logs() {
save_ret=${?}
match=
+ timestamp=-d
for i in "${@}"; do
- if [ X"${i}" != X"${i#-}" ] ; then
+ if [ X"${i}" != X"${i#-t}" ]; then
+ timestamp="${i}"
+ elif [ X"${i}" != X"${i#-}" ]; then
match="${match}
${i#-}"
else
@@ -323,12 +364,13 @@
bootstat: Canonical boot reason: ${i}"
fi
done
- adb logcat -b all -d |
+ adb logcat -b all ${timestamp} |
grep bootstat[^e] |
grep -v -F "bootstat: Service started: /system/bin/bootstat --record_boot_complete${match}
bootstat: Failed to read /data/misc/bootstat/post_decrypt_time_elapsed: No such file or directory
bootstat: Failed to parse boot time record: /data/misc/bootstat/post_decrypt_time_elapsed
bootstat: Service started: /system/bin/bootstat --record_boot_reason
+bootstat: Service started: /system/bin/bootstat --set_system_boot_reason
bootstat: Service started: /system/bin/bootstat --record_time_since_factory_reset
bootstat: Service started: /system/bin/bootstat -l
bootstat: Service started: /system/bin/bootstat --set_system_boot_reason --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l
@@ -341,6 +383,8 @@
init : processing action (post-fs-data) from (/system/etc/init/bootstat.rc
init : processing action (boot) from (/system/etc/init/bootstat.rc
init : processing action (ro.boot.bootreason=*) from (/system/etc/init/bootstat.rc
+init : processing action (ro.boot.bootreason=* && post-fs) from (/system/etc/init/bootstat.rc
+init : processing action (zygote-start) from (/system/etc/init/bootstat.rc
init : processing action (sys.boot_completed=1 && sys.logbootcomplete=1) from (/system/etc/init/bootstat.rc
(/system/bin/bootstat --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l)'
(/system/bin/bootstat --set_system_boot_reason --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l)'
@@ -355,6 +399,8 @@
(/system/bin/bootstat --record_boot_reason)' (pid${SPACE}
(/system/bin/bootstat --record_time_since_factory_reset)'...
(/system/bin/bootstat --record_time_since_factory_reset)' (pid${SPACE}
+ (/system/bin/bootstat --set_system_boot_reason)'...
+ (/system/bin/bootstat --set_system_boot_reason)' (pid${SPACE}
(/system/bin/bootstat -l)'...
(/system/bin/bootstat -l)' (pid " |
grep -v 'bootstat: Unknown boot reason: $' # Hikey Special
@@ -613,7 +659,7 @@
test_optional_ota() {
checkDebugBuild || return
duration_test
- adb shell su root touch /data/misc/bootstat/build_date >&2
+ adb_su touch /data/misc/bootstat/build_date >&2 </dev/null
adb reboot ota
wait_for_screen
EXPECT_PROPERTY sys.boot.reason reboot,ota
@@ -679,7 +725,7 @@
test_factory_reset() {
checkDebugBuild || return
duration_test
- adb shell su root rm /data/misc/bootstat/build_date >&2
+ adb_su rm /data/misc/bootstat/build_date >&2 </dev/null
adb reboot >&2
wait_for_screen
EXPECT_PROPERTY sys.boot.reason reboot,factory_reset
@@ -715,7 +761,7 @@
wait_for_screen
( exit ${save_ret} ) # because one can not just do ?=${save_ret}
EXPECT_PROPERTY sys.boot.reason reboot,factory_reset
- EXPECT_PROPERTY sys.boot.reason.last ""
+ EXPECT_PROPERTY sys.boot.reason.last "\(\|bootloader\)"
check_boilerplate_properties
report_bootstat_logs reboot,factory_reset bootloader \
"-bootstat: Failed to read /data/misc/bootstat/last_boot_time_utc: No such file or directory" \
@@ -766,12 +812,12 @@
enterPstore
# Send it _many_ times to combat devices with flakey pstore
for i in a b c d e f g h i j k l m n o p q r s t u v w x y z; do
- echo 'healthd: battery l=2 ' | adb shell su root tee /dev/kmsg >/dev/null
+ echo 'healthd: battery l=2 ' | adb_su tee /dev/kmsg >/dev/null
done
adb reboot cold >&2
adb wait-for-device
wait_for_screen
- adb shell su root \
+ adb_su </dev/null \
cat /proc/fs/pstore/console-ramoops \
/proc/fs/pstore/console-ramoops-0 2>/dev/null |
grep 'healthd: battery l=' |
@@ -780,7 +826,7 @@
if ! EXPECT_PROPERTY sys.boot.reason reboot,battery >/dev/null 2>/dev/null; then
# retry
for i in a b c d e f g h i j k l m n o p q r s t u v w x y z; do
- echo 'healthd: battery l=2 ' | adb shell su root tee /dev/kmsg >/dev/null
+ echo 'healthd: battery l=2 ' | adb_su tee /dev/kmsg >/dev/null
done
adb reboot cold >&2
adb wait-for-device
@@ -806,7 +852,7 @@
test_optional_battery() {
duration_test ">60"
echo " power on request" >&2
- adb shell setprop sys.powerctl shutdown,battery
+ adb_sh setprop sys.powerctl shutdown,battery </dev/null
sleep 5
echo -n "WARNING: Please power device back up, waiting ... " >&2
wait_for_screen -n >&2
@@ -827,7 +873,7 @@
test_optional_battery_thermal() {
duration_test ">60"
echo " power on request" >&2
- adb shell setprop sys.powerctl shutdown,thermal,battery
+ adb_sh setprop sys.powerctl shutdown,thermal,battery </dev/null
sleep 5
echo -n "WARNING: Please power device back up, waiting ... " >&2
wait_for_screen -n >&2
@@ -866,7 +912,7 @@
panic_msg="\(kernel_panic,sysrq\|kernel_panic\)"
pstore_ok=true
fi
- echo c | adb shell su root tee /proc/sysrq-trigger >/dev/null
+ echo c | adb_su tee /proc/sysrq-trigger >/dev/null
wait_for_screen
EXPECT_PROPERTY sys.boot.reason ${panic_msg}
EXPECT_PROPERTY sys.boot.reason.last ${panic_msg}
@@ -893,8 +939,8 @@
panic_msg="\(kernel_panic,sysrq,test\|kernel_panic\)"
pstore_ok=true
fi
- echo "SysRq : Trigger a crash : 'test'" | adb shell su root tee /dev/kmsg
- echo c | adb shell su root tee /proc/sysrq-trigger >/dev/null
+ echo "SysRq : Trigger a crash : 'test'" | adb_su tee /dev/kmsg
+ echo c | adb_su tee /proc/sysrq-trigger >/dev/null
wait_for_screen
EXPECT_PROPERTY sys.boot.reason ${panic_msg}
EXPECT_PROPERTY sys.boot.reason.last ${panic_msg}
@@ -924,7 +970,7 @@
pstore_ok=true
fi
echo "Kernel panic - not syncing: hung_task: blocked tasks" |
- adb shell su root tee /dev/kmsg
+ adb_su tee /dev/kmsg
adb reboot warm
wait_for_screen
EXPECT_PROPERTY sys.boot.reason ${panic_msg}
@@ -956,7 +1002,7 @@
test_thermal_shutdown() {
duration_test ">60"
echo " power on request" >&2
- adb shell setprop sys.powerctl shutdown,thermal
+ adb_sh setprop sys.powerctl shutdown,thermal </dev/null
sleep 5
echo -n "WARNING: Please power device back up, waiting ... " >&2
wait_for_screen -n >&2
@@ -977,7 +1023,7 @@
test_userrequested_shutdown() {
duration_test ">60"
echo " power on request" >&2
- adb shell setprop sys.powerctl shutdown,userrequested
+ adb_sh setprop sys.powerctl shutdown,userrequested </dev/null
sleep 5
echo -n "WARNING: Please power device back up, waiting ... " >&2
wait_for_screen -n >&2
@@ -996,7 +1042,7 @@
- NB: should report reboot,shell" ]
test_shell_reboot() {
duration_test
- adb shell reboot
+ adb_sh reboot </dev/null
wait_for_screen
EXPECT_PROPERTY sys.boot.reason reboot,shell
EXPECT_PROPERTY sys.boot.reason.last reboot,shell
@@ -1032,7 +1078,7 @@
test_optional_rescueparty() {
blind_reboot_test
echo "WARNING: legacy devices are allowed to fail following ro.boot.bootreason result" >&2
- EXPECT_PROPERTY ro.boot.bootreason reboot,rescueparty
+ EXPECT_PROPERTY ro.boot.bootreason '\(reboot\|reboot,rescueparty\)'
}
[ "USAGE: test_Its_Just_So_Hard_reboot
@@ -1049,7 +1095,7 @@
else
duration_test `expr ${DURATION_DEFAULT} + ${DURATION_DEFAULT}`
fi
- adb shell 'reboot "Its Just So Hard"'
+ adb_sh 'reboot "Its Just So Hard"' </dev/null
wait_for_screen
EXPECT_PROPERTY sys.boot.reason reboot,its_just_so_hard
EXPECT_PROPERTY sys.boot.reason.last reboot,its_just_so_hard
@@ -1146,7 +1192,121 @@
run_bootloader
}
-[ "USAGE: ${0##*/} [-s SERIAL] [tests]
+[ "USAGE: run_kBootReasonMap [--boot_reason_enum] value expected
+
+bootloader boot reason injection tests:
+- if --boot_reason_enum run bootstat executable for result instead.
+- inject boot reason into sys.boot.reason
+- run bootstat --set_system_boot_reason
+- check for expected enum
+- " ]
+run_kBootReasonMap() {
+ if [ X"--boot_reason_enum" = X"${1}" ]; then
+ shift
+ local sys_expected="${1}"
+ shift
+ local enum_expected="${1}"
+ adb_su bootstat --boot_reason_enum="${sys_expected}" |
+ (
+ local retval=-1
+ while read -r id match; do
+ if [ ${retval} = -1 -a ${enum_expected} = ${id} ]; then
+ retval=0
+ fi
+ if [ ${enum_expected} != ${id} ]; then
+ echo "ERROR: ${enum_expected} ${sys_expected} got ${id} ${match}" >&2
+ retval=1
+ fi
+ done
+ exit ${retval}
+ )
+ return
+ fi
+ local sys_expected="${1}"
+ shift
+ local enum_expected="${1}"
+ adb_su setprop sys.boot.reason "${sys_expected}" </dev/null
+ adb_su bootstat --record_boot_reason </dev/null
+ # Check values
+ EXPECT_PROPERTY sys.boot.reason "${sys_expected}"
+ local retval=${?}
+ local result=`adb_su stat -c %Y /data/misc/bootstat/system_boot_reason </dev/null 2>/dev/null`
+ [ "${enum_expected}" = "${result}" ] ||
+ (
+ [ -n "${result}" ] || result="<nothing>"
+ echo "ERROR: ${enum_expected} ${sys_expected} got ${result}" >&2
+ false
+ ) ||
+ retval=${?}
+ return ${retval}
+}
+
+[ "USAGE: filter_kBootReasonMap </dev/stdin >/dev/stdout
+
+convert any regex expressions into a series of non-regex test strings" ]
+filter_kBootReasonMap() {
+ while read -r id match; do
+ case ${match} in
+ 'reboot,[empty]')
+ echo ${id} # matches b/c of special case
+ echo ${id} reboot,y # matches b/c of regex
+ echo 1 reboot,empty # negative test (ID for unknown is 1)
+ ;;
+ reboot)
+ echo 1 reboog # negative test (ID for unknown is 1)
+ ;;
+ 'reboot,pmic_off_fault,.*')
+ echo ${id} reboot,pmic_off_fault,hello,world
+ echo ${id} reboot,pmic_off_fault,
+ echo 1 reboot,pmic_off_fault
+ ;;
+ esac
+ echo ${id} "${match}" # matches b/c of exact
+ done
+}
+
+[ "USAGE: test_kBootReasonMap
+
+kBootReasonMap test
+- (wait until screen is up, boot has completed)
+- read bootstat for kBootReasonMap entries and test them all" ]
+test_kBootReasonMap() {
+ checkDebugBuild || return
+ duration_test 15
+ local tempfile="`mktemp`"
+ local arg=--boot_reason_enum
+ adb_su bootstat ${arg} </dev/null 2>/dev/null |
+ filter_kBootReasonMap >${tempfile}
+ if [ ! -s "${tempfile}" ]; then
+ wait_for_screen
+ arg=
+ sed -n <${progpath}bootstat.cpp \
+ '/kBootReasonMap = {/,/^};/s/.*{"\([^"]*\)", *\([0-9][0-9]*\)},.*/\2 \1/p' |
+ sed 's/\\\\/\\/g' |
+ filter_kBootReasonMap >${tempfile}
+ fi
+ T=`adb_date`
+ retval=0
+ while read -r enum string; do
+ if [ X"${string}" != X"${string#*[[].[]]}" -o X"${string}" != X"${string#*\\.}" ]; then
+ if [ 'reboot\.empty' != "${string}" ]; then
+ echo "WARNING: regex snuck through filter_kBootReasonMap ${enum} ${string}" >&2
+ enum=1
+ fi
+ fi
+ run_kBootReasonMap ${arg} "${string}" "${enum}" </dev/null || retval=${?}
+ done <${tempfile}
+ rm ${tempfile}
+ ( exit ${retval} )
+ # See filter_kBootReasonMap() for negative tests and add them here too
+ report_bootstat_logs -t${T} \
+ '-bootstat: Service started: bootstat --boot_reason_enum=' \
+ '-bootstat: Unknown boot reason: reboot,empty' \
+ '-bootstat: Unknown boot reason: reboog' \
+ '-bootstat: Unknown boot reason: reboot,pmic_off_fault'
+}
+
+[ "USAGE: ${progname} [-s SERIAL] [tests]...
Mainline executive to run the above tests" ]
@@ -1161,7 +1321,7 @@
if [ X"--macros" != X"${1}" ]; then
if [ X"--help" = X"${1}" -o X"-h" = X"${1}" -o X"-?" = X"${1}" ]; then
- echo "USAGE: ${0##*/} [-s SERIAL] [tests]"
+ echo "USAGE: ${progname} [-s SERIAL] [tests]..."
echo tests - `sed -n 's/^test_\([^ ()]*\)() {/\1/p' $0 </dev/null`
exit 0
fi
@@ -1210,7 +1370,7 @@
Its_Just_So_Hard_reboot bootloader_normal bootloader_watchdog \
bootloader_kernel_panic bootloader_oem_powerkey \
bootloader_wdog_reset bootloader_cold bootloader_warm \
- bootloader_hard bootloader_recovery
+ bootloader_hard bootloader_recovery kBootReasonMap
fi
if [ X"nothing" = X"${1}" ]; then
shift 1
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 1ce0ec4..558e6c4 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -89,7 +89,7 @@
}
void ShowHelp(const char* cmd) {
- fprintf(stderr, "Usage: %s [options]\n", cmd);
+ fprintf(stderr, "Usage: %s [options]...\n", cmd);
fprintf(stderr,
"options include:\n"
" -h, --help Show this help\n"
@@ -99,7 +99,8 @@
" --value Optional value to associate with the boot event\n"
" --record_boot_complete Record metrics related to the time for the device boot\n"
" --record_boot_reason Record the reason why the device booted\n"
- " --record_time_since_factory_reset Record the time since the device was reset\n");
+ " --record_time_since_factory_reset Record the time since the device was reset\n"
+ " --boot_reason_enum=<reason> Report the match to the kBootReasonMap table\n");
}
// Constructs a readable, printable string from the givencommand line
@@ -120,9 +121,10 @@
// A mapping from boot reason string, as read from the ro.boot.bootreason
// system property, to a unique integer ID. Viewers of log data dashboards for
// the boot_reason metric may refer to this mapping to discern the histogram
-// values.
+// values. Regex matching, to manage the scale, as a minimum require either
+// [, \ or * to be present in the string to switch to checking.
const std::map<std::string, int32_t> kBootReasonMap = {
- {"empty", kEmptyBootReason},
+ {"reboot,[empty]", kEmptyBootReason},
{"__BOOTSTAT_UNKNOWN__", kUnknownBootReason},
{"normal", 2},
{"recovery", 3},
@@ -299,6 +301,9 @@
{"reboot,dm-verity_device_corrupted", 172},
{"reboot,dm-verity_enforcing", 173},
{"reboot,keys_clear", 174},
+ {"reboot,pmic_off_fault,.*", 175},
+ {"reboot,pmic_off_s3rst,.*", 176},
+ {"reboot,pmic_off_other,.*", 177},
};
// Converts a string value representing the reason the system booted to an
@@ -314,6 +319,16 @@
return kEmptyBootReason;
}
+ for (const auto& [match, id] : kBootReasonMap) {
+ // Regex matches as a minimum require either [, \ or * to be present.
+ if (match.find_first_of("[\\*") == match.npos) continue;
+ // enforce match from beginning to end
+ auto exact = match;
+ if (exact[0] != '^') exact = "^" + exact;
+ if (exact[exact.size() - 1] != '$') exact = exact + "$";
+ if (std::regex_search(boot_reason, std::regex(exact))) return id;
+ }
+
LOG(INFO) << "Unknown boot reason: " << boot_reason;
return kUnknownBootReason;
}
@@ -1266,6 +1281,19 @@
boot_event_store.AddBootEventWithValue("time_since_factory_reset", time_since_factory_reset);
}
+// List the associated boot reason(s), if arg is nullptr then all.
+void PrintBootReasonEnum(const char* arg) {
+ int value = -1;
+ if (arg != nullptr) {
+ value = BootReasonStrToEnum(arg);
+ }
+ for (const auto& [match, id] : kBootReasonMap) {
+ if ((value < 0) || (value == id)) {
+ printf("%u\t%s\n", id, match.c_str());
+ }
+ }
+}
+
} // namespace
int main(int argc, char** argv) {
@@ -1280,6 +1308,7 @@
static const char boot_complete_str[] = "record_boot_complete";
static const char boot_reason_str[] = "record_boot_reason";
static const char factory_reset_str[] = "record_time_since_factory_reset";
+ static const char boot_reason_enum_str[] = "boot_reason_enum";
static const struct option long_options[] = {
// clang-format off
{ "help", no_argument, NULL, 'h' },
@@ -1291,6 +1320,7 @@
{ boot_complete_str, no_argument, NULL, 0 },
{ boot_reason_str, no_argument, NULL, 0 },
{ factory_reset_str, no_argument, NULL, 0 },
+ { boot_reason_enum_str, optional_argument, NULL, 0 },
{ NULL, 0, NULL, 0 }
// clang-format on
};
@@ -1315,6 +1345,8 @@
RecordBootReason();
} else if (option_name == factory_reset_str) {
RecordFactoryReset();
+ } else if (option_name == boot_reason_enum_str) {
+ PrintBootReasonEnum(optarg);
} else {
LOG(ERROR) << "Invalid option: " << option_name;
}
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/types.h b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
index 70583af..eb4b1b8 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/types.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
@@ -23,6 +23,9 @@
struct ThreadInfo {
std::unique_ptr<unwindstack::Regs> registers;
+
+ pid_t uid;
+
pid_t tid;
std::string thread_name;
diff --git a/debuggerd/libdebuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp
index 3196ce8..88c206f 100644
--- a/debuggerd/libdebuggerd/test/tombstone_test.cpp
+++ b/debuggerd/libdebuggerd/test/tombstone_test.cpp
@@ -343,6 +343,16 @@
ASSERT_STREQ(expected.c_str(), amfd_data_.c_str());
}
+TEST_F(TombstoneTest, dump_thread_info_uid) {
+ dump_thread_info(&log_, ThreadInfo{.uid = 1,
+ .pid = 2,
+ .tid = 3,
+ .thread_name = "some_thread",
+ .process_name = "some_process"});
+ std::string expected = "pid: 2, tid: 3, name: some_thread >>> some_process <<<\nuid: 1\n";
+ ASSERT_STREQ(expected.c_str(), amfd_data_.c_str());
+}
+
TEST_F(TombstoneTest, dump_timestamp) {
setenv("TZ", "UTC", 1);
tzset();
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index d1726cd..d246722 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -151,6 +151,7 @@
_LOG(log, logtype::HEADER, "pid: %d, tid: %d, name: %s >>> %s <<<\n", thread_info.pid,
thread_info.tid, thread_info.thread_name.c_str(), thread_info.process_name.c_str());
+ _LOG(log, logtype::HEADER, "uid: %d\n", thread_info.uid);
}
static void dump_stack_segment(log_t* log, unwindstack::Maps* maps, unwindstack::Memory* memory,
@@ -615,6 +616,7 @@
void engrave_tombstone_ucontext(int tombstone_fd, uint64_t abort_msg_address, siginfo_t* siginfo,
ucontext_t* ucontext) {
+ pid_t uid = getuid();
pid_t pid = getpid();
pid_t tid = gettid();
@@ -636,6 +638,7 @@
std::map<pid_t, ThreadInfo> threads;
threads[gettid()] = ThreadInfo{
.registers = std::move(regs),
+ .uid = uid,
.tid = tid,
.thread_name = thread_name,
.pid = pid,
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index f8f7eb3..25df451 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -502,9 +502,8 @@
static bool UnzipToMemory(ZipArchiveHandle zip, const std::string& entry_name,
std::vector<char>* out) {
- ZipString zip_entry_name(entry_name.c_str());
ZipEntry zip_entry;
- if (FindEntry(zip, zip_entry_name, &zip_entry) != 0) {
+ if (FindEntry(zip, entry_name, &zip_entry) != 0) {
fprintf(stderr, "archive does not contain '%s'\n", entry_name.c_str());
return false;
}
@@ -614,9 +613,8 @@
static int unzip_to_file(ZipArchiveHandle zip, const char* entry_name) {
unique_fd fd(make_temporary_fd(entry_name));
- ZipString zip_entry_name(entry_name);
ZipEntry zip_entry;
- if (FindEntry(zip, zip_entry_name, &zip_entry) != 0) {
+ if (FindEntry(zip, entry_name, &zip_entry) != 0) {
fprintf(stderr, "archive does not contain '%s'\n", entry_name);
errno = ENOENT;
return -1;
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 4ee9624..ffde114 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -121,9 +121,14 @@
shared_libs: [
"libbootloader_message",
"libbase",
+ "libcutils",
"libcrypto",
+ "libext4_utils",
"libfec",
"libfs_mgr",
+ "liblog",
+ "liblp",
+ "libselinux",
],
header_libs: [
"libcutils_headers",
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index c1aafda..6f24fe1 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -1610,38 +1610,6 @@
return ret;
}
-bool fs_mgr_load_verity_state(int* mode) {
- /* return the default mode, unless any of the verified partitions are in
- * logging mode, in which case return that */
- *mode = VERITY_MODE_DEFAULT;
-
- Fstab fstab;
- if (!ReadDefaultFstab(&fstab)) {
- LERROR << "Failed to read default fstab";
- return false;
- }
-
- for (const auto& entry : fstab) {
- if (entry.fs_mgr_flags.avb) {
- *mode = VERITY_MODE_RESTART; // avb only supports restart mode.
- break;
- } else if (!entry.fs_mgr_flags.verify) {
- continue;
- }
-
- int current;
- if (load_verity_state(entry, ¤t) < 0) {
- continue;
- }
- if (current != VERITY_MODE_DEFAULT) {
- *mode = current;
- break;
- }
- }
-
- return true;
-}
-
bool fs_mgr_is_verity_enabled(const FstabEntry& entry) {
if (!entry.fs_mgr_flags.verify && !entry.fs_mgr_flags.avb) {
return false;
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 4043fc6..78455d4 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -38,6 +38,7 @@
using android::base::ParseByteCount;
using android::base::ParseInt;
+using android::base::ReadFileToString;
using android::base::Split;
using android::base::StartsWith;
@@ -260,10 +261,6 @@
LWARNING << "Warning: zramsize= flag malformed: " << arg;
}
}
- } else if (StartsWith(flag, "verify=")) {
- // If the verify flag is followed by an = and the location for the verity state.
- entry->fs_mgr_flags.verify = true;
- entry->verity_loc = arg;
} else if (StartsWith(flag, "forceencrypt=")) {
// The forceencrypt flag is followed by an = and the location of the keys.
entry->fs_mgr_flags.force_crypt = true;
@@ -660,6 +657,8 @@
TransformFstabForGsi(fstab);
}
+ SkipMountingPartitions(fstab);
+
return true;
}
@@ -687,6 +686,36 @@
return false;
}
+ SkipMountingPartitions(fstab);
+
+ return true;
+}
+
+// For GSI to skip mounting /product and /product_services, until there are
+// well-defined interfaces between them and /system. Otherwise, the GSI flashed
+// on /system might not be able to work with /product and /product_services.
+// When they're skipped here, /system/product and /system/product_services in
+// GSI will be used.
+bool SkipMountingPartitions(Fstab* fstab) {
+ constexpr const char kSkipMountConfig[] = "/system/etc/init/config/skip_mount.cfg";
+
+ std::string skip_config;
+ if (!ReadFileToString(kSkipMountConfig, &skip_config)) {
+ return true;
+ }
+
+ for (const auto& skip_mount_point : Split(skip_config, "\n")) {
+ if (skip_mount_point.empty()) {
+ continue;
+ }
+ auto it = std::remove_if(fstab->begin(), fstab->end(),
+ [&skip_mount_point](const auto& entry) {
+ return entry.mount_point == skip_mount_point;
+ });
+ fstab->erase(it, fstab->end());
+ LOG(INFO) << "Skip mounting partition: " << skip_mount_point;
+ }
+
return true;
}
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index e46e497..71c4072 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -646,6 +646,25 @@
return true;
}
+static void TruncatePartitionsWithSuffix(MetadataBuilder* builder, const std::string& suffix) {
+ auto& dm = DeviceMapper::Instance();
+
+ // Remove <other> partitions
+ for (const auto& group : builder->ListGroups()) {
+ for (const auto& part : builder->ListPartitionsInGroup(group)) {
+ const auto& name = part->name();
+ if (!android::base::EndsWith(name, suffix)) {
+ continue;
+ }
+ if (dm.GetState(name) != DmDeviceState::INVALID && !DestroyLogicalPartition(name, 2s)) {
+ continue;
+ }
+ builder->ResizePartition(builder->FindPartition(name), 0);
+ }
+ }
+}
+
+// This is where we find and steal backing storage from the system.
bool fs_mgr_overlayfs_create_scratch(const Fstab& fstab, std::string* scratch_device,
bool* partition_exists, bool* change) {
*scratch_device = fs_mgr_overlayfs_scratch_device();
@@ -692,15 +711,24 @@
// the adb remount overrides :-( .
auto margin_size = uint64_t(3 * 256 * 1024);
BlockDeviceInfo info;
- if (builder->GetBlockDeviceInfo(partition_name, &info)) {
+ if (builder->GetBlockDeviceInfo(fs_mgr_get_super_partition_name(slot_number), &info)) {
margin_size = 3 * info.logical_block_size;
}
partition_size = std::max(std::min(kMinimumSize, partition_size - margin_size),
partition_size / 2);
if (partition_size > partition->size()) {
if (!builder->ResizePartition(partition, partition_size)) {
- LERROR << "resize " << partition_name;
- return false;
+ // Try to free up space by deallocating partitions in the other slot.
+ TruncatePartitionsWithSuffix(builder.get(), fs_mgr_get_other_slot_suffix());
+
+ partition_size =
+ builder->AllocatableSpace() - builder->UsedSpace() + partition->size();
+ partition_size = std::max(std::min(kMinimumSize, partition_size - margin_size),
+ partition_size / 2);
+ if (!builder->ResizePartition(partition, partition_size)) {
+ LERROR << "resize " << partition_name;
+ return false;
+ }
}
if (!partition_create) DestroyLogicalPartition(partition_name, 10s);
changed = true;
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 70abf5b..c36fd3d 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -99,7 +99,6 @@
bool fs_mgr_is_device_unlocked();
const std::string& get_android_dt_dir();
bool is_dt_compatible();
-int load_verity_state(const android::fs_mgr::FstabEntry& entry, int* mode);
bool fs_mgr_is_ext4(const std::string& blk_device);
bool fs_mgr_is_f2fs(const std::string& blk_device);
diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp
index cbe2008..00334bc 100644
--- a/fs_mgr/fs_mgr_remount.cpp
+++ b/fs_mgr/fs_mgr_remount.cpp
@@ -249,6 +249,7 @@
// Check verity and optionally setup overlayfs backing.
auto reboot_later = false;
+ auto user_please_reboot_later = false;
auto uses_overlayfs = fs_mgr_overlayfs_valid() != OverlayfsValidResult::kNotSupported;
for (auto it = partitions.begin(); it != partitions.end();) {
auto& entry = *it;
@@ -262,7 +263,7 @@
false);
avb_ops_user_free(ops);
if (ret) {
- LOG(WARNING) << "Disable verity for " << mount_point;
+ LOG(WARNING) << "Disabling verity for " << mount_point;
reboot_later = can_reboot;
if (reboot_later) {
// w/o overlayfs available, also check for dedupe
@@ -272,20 +273,22 @@
}
reboot(false);
}
+ user_please_reboot_later = true;
} else if (fs_mgr_set_blk_ro(entry.blk_device, false)) {
fec::io fh(entry.blk_device.c_str(), O_RDWR);
if (fh && fh.set_verity_status(false)) {
- LOG(WARNING) << "Disable verity for " << mount_point;
+ LOG(WARNING) << "Disabling verity for " << mount_point;
reboot_later = can_reboot;
if (reboot_later && !uses_overlayfs) {
++it;
continue;
}
+ user_please_reboot_later = true;
}
}
}
}
- LOG(ERROR) << "Skipping " << mount_point;
+ LOG(ERROR) << "Skipping " << mount_point << " for remount";
it = partitions.erase(it);
continue;
}
@@ -307,6 +310,10 @@
if (partitions.empty()) {
if (reboot_later) reboot(false);
+ if (user_please_reboot_later) {
+ LOG(INFO) << "Now reboot your device for settings to take effect";
+ return 0;
+ }
LOG(WARNING) << "No partitions to remount";
return retval;
}
@@ -383,6 +390,10 @@
}
if (reboot_later) reboot(false);
+ if (user_please_reboot_later) {
+ LOG(INFO) << "Now reboot your device for settings to take effect";
+ return 0;
+ }
return retval;
}
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index 3f09157..1deb1ac 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -275,248 +275,6 @@
return 0;
}
-static int check_verity_restart(const char *fname)
-{
- char buffer[VERITY_KMSG_BUFSIZE + 1];
- int fd;
- int rc = 0;
- ssize_t size;
- struct stat s;
-
- fd = TEMP_FAILURE_RETRY(open(fname, O_RDONLY | O_CLOEXEC));
-
- if (fd == -1) {
- if (errno != ENOENT) {
- PERROR << "Failed to open " << fname;
- }
- goto out;
- }
-
- if (fstat(fd, &s) == -1) {
- PERROR << "Failed to fstat " << fname;
- goto out;
- }
-
- size = VERITY_KMSG_BUFSIZE;
-
- if (size > s.st_size) {
- size = s.st_size;
- }
-
- if (lseek(fd, s.st_size - size, SEEK_SET) == -1) {
- PERROR << "Failed to lseek " << (intmax_t)(s.st_size - size) << " " << fname;
- goto out;
- }
-
- if (!android::base::ReadFully(fd, buffer, size)) {
- PERROR << "Failed to read " << size << " bytes from " << fname;
- goto out;
- }
-
- buffer[size] = '\0';
-
- if (strstr(buffer, VERITY_KMSG_RESTART) != NULL) {
- rc = 1;
- }
-
-out:
- if (fd != -1) {
- close(fd);
- }
-
- return rc;
-}
-
-static int was_verity_restart()
-{
- static const char* files[] = {
- // clang-format off
- "/sys/fs/pstore/console-ramoops-0",
- "/sys/fs/pstore/console-ramoops",
- "/proc/last_kmsg",
- NULL
- // clang-format on
- };
- int i;
-
- for (i = 0; files[i]; ++i) {
- if (check_verity_restart(files[i])) {
- return 1;
- }
- }
-
- return 0;
-}
-
-static int metadata_add(FILE *fp, long start, const char *tag,
- unsigned int length, off64_t *offset)
-{
- if (fseek(fp, start, SEEK_SET) < 0 ||
- fprintf(fp, "%s %u\n", tag, length) < 0) {
- return -1;
- }
-
- *offset = ftell(fp);
-
- if (fseek(fp, length, SEEK_CUR) < 0 ||
- fprintf(fp, METADATA_EOD " 0\n") < 0) {
- return -1;
- }
-
- return 0;
-}
-
-static int metadata_find(const char *fname, const char *stag,
- unsigned int slength, off64_t *offset)
-{
- char tag[METADATA_TAG_MAX_LENGTH + 1];
- int rc = -1;
- int n;
- long start = 0x4000; /* skip cryptfs metadata area */
- uint32_t magic;
- unsigned int length = 0;
-
- if (!fname) {
- return -1;
- }
-
- auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(fname, "re+"), fclose};
-
- if (!fp) {
- PERROR << "Failed to open " << fname;
- return -1;
- }
-
- /* check magic */
- if (fseek(fp.get(), start, SEEK_SET) < 0 || fread(&magic, sizeof(magic), 1, fp.get()) != 1) {
- PERROR << "Failed to read magic from " << fname;
- return -1;
- }
-
- if (magic != METADATA_MAGIC) {
- magic = METADATA_MAGIC;
-
- if (fseek(fp.get(), start, SEEK_SET) < 0 ||
- fwrite(&magic, sizeof(magic), 1, fp.get()) != 1) {
- PERROR << "Failed to write magic to " << fname;
- return -1;
- }
-
- rc = metadata_add(fp.get(), start + sizeof(magic), stag, slength, offset);
- if (rc < 0) {
- PERROR << "Failed to add metadata to " << fname;
- }
-
- return rc;
- }
-
- start += sizeof(magic);
-
- while (1) {
- n = fscanf(fp.get(), "%" STRINGIFY(METADATA_TAG_MAX_LENGTH) "s %u\n", tag, &length);
-
- if (n == 2 && strcmp(tag, METADATA_EOD)) {
- /* found a tag */
- start = ftell(fp.get());
-
- if (!strcmp(tag, stag) && length == slength) {
- *offset = start;
- return 0;
- }
-
- start += length;
-
- if (fseek(fp.get(), length, SEEK_CUR) < 0) {
- PERROR << "Failed to seek " << fname;
- return -1;
- }
- } else {
- rc = metadata_add(fp.get(), start, stag, slength, offset);
- if (rc < 0) {
- PERROR << "Failed to write metadata to " << fname;
- }
- return rc;
- }
- }
-}
-
-static int write_verity_state(const char *fname, off64_t offset, int32_t mode)
-{
- int fd;
- int rc = -1;
- struct verity_state s = { VERITY_STATE_HEADER, VERITY_STATE_VERSION, mode };
-
- fd = TEMP_FAILURE_RETRY(open(fname, O_WRONLY | O_SYNC | O_CLOEXEC));
-
- if (fd == -1) {
- PERROR << "Failed to open " << fname;
- goto out;
- }
-
- if (TEMP_FAILURE_RETRY(pwrite64(fd, &s, sizeof(s), offset)) != sizeof(s)) {
- PERROR << "Failed to write " << sizeof(s) << " bytes to " << fname
- << " to offset " << offset;
- goto out;
- }
-
- rc = 0;
-
-out:
- if (fd != -1) {
- close(fd);
- }
-
- return rc;
-}
-
-static int read_verity_state(const char *fname, off64_t offset, int *mode)
-{
- int fd = -1;
- int rc = -1;
- struct verity_state s;
-
- fd = TEMP_FAILURE_RETRY(open(fname, O_RDONLY | O_CLOEXEC));
-
- if (fd == -1) {
- PERROR << "Failed to open " << fname;
- goto out;
- }
-
- if (TEMP_FAILURE_RETRY(pread64(fd, &s, sizeof(s), offset)) != sizeof(s)) {
- PERROR << "Failed to read " << sizeof(s) << " bytes from " << fname
- << " offset " << offset;
- goto out;
- }
-
- if (s.header != VERITY_STATE_HEADER) {
- /* space allocated, but no state written. write default state */
- *mode = VERITY_MODE_DEFAULT;
- rc = write_verity_state(fname, offset, *mode);
- goto out;
- }
-
- if (s.version != VERITY_STATE_VERSION) {
- LERROR << "Unsupported verity state version (" << s.version << ")";
- goto out;
- }
-
- if (s.mode < VERITY_MODE_EIO ||
- s.mode > VERITY_MODE_LAST) {
- LERROR << "Unsupported verity mode (" << s.mode << ")";
- goto out;
- }
-
- *mode = s.mode;
- rc = 0;
-
-out:
- if (fd != -1) {
- close(fd);
- }
-
- return rc;
-}
-
static int read_partition(const char *path, uint64_t size)
{
char buf[READ_BUF_SIZE];
@@ -540,119 +298,23 @@
return 0;
}
-static int compare_last_signature(const FstabEntry& entry, int* match) {
- char tag[METADATA_TAG_MAX_LENGTH + 1];
- int fd = -1;
- int rc = -1;
- off64_t offset = 0;
- struct fec_handle *f = NULL;
- struct fec_verity_metadata verity;
- uint8_t curr[SHA256_DIGEST_LENGTH];
- uint8_t prev[SHA256_DIGEST_LENGTH];
-
- *match = 1;
-
- if (fec_open(&f, entry.blk_device.c_str(), O_RDONLY, FEC_VERITY_DISABLE, FEC_DEFAULT_ROOTS) ==
- -1) {
- PERROR << "Failed to open '" << entry.blk_device << "'";
- return rc;
- }
-
- // read verity metadata
- if (fec_verity_get_metadata(f, &verity) == -1) {
- PERROR << "Failed to get verity metadata '" << entry.blk_device << "'";
- goto out;
- }
-
- SHA256(verity.signature, sizeof(verity.signature), curr);
-
- if (snprintf(tag, sizeof(tag), VERITY_LASTSIG_TAG "_%s", basename(entry.mount_point.c_str())) >=
- (int)sizeof(tag)) {
- LERROR << "Metadata tag name too long for " << entry.mount_point;
- goto out;
- }
-
- if (metadata_find(entry.verity_loc.c_str(), tag, SHA256_DIGEST_LENGTH, &offset) < 0) {
- goto out;
- }
-
- fd = TEMP_FAILURE_RETRY(open(entry.verity_loc.c_str(), O_RDWR | O_SYNC | O_CLOEXEC));
-
- if (fd == -1) {
- PERROR << "Failed to open " << entry.verity_loc;
- goto out;
- }
-
- if (TEMP_FAILURE_RETRY(pread64(fd, prev, sizeof(prev), offset)) != sizeof(prev)) {
- PERROR << "Failed to read " << sizeof(prev) << " bytes from " << entry.verity_loc
- << " offset " << offset;
- goto out;
- }
-
- *match = !memcmp(curr, prev, SHA256_DIGEST_LENGTH);
-
- if (!*match) {
- /* update current signature hash */
- if (TEMP_FAILURE_RETRY(pwrite64(fd, curr, sizeof(curr),
- offset)) != sizeof(curr)) {
- PERROR << "Failed to write " << sizeof(curr) << " bytes to " << entry.verity_loc
- << " offset " << offset;
- goto out;
- }
- }
-
- rc = 0;
-
-out:
- fec_close(f);
- return rc;
-}
-
-static int get_verity_state_offset(const FstabEntry& entry, off64_t* offset) {
- char tag[METADATA_TAG_MAX_LENGTH + 1];
-
- if (snprintf(tag, sizeof(tag), VERITY_STATE_TAG "_%s", basename(entry.mount_point.c_str())) >=
- (int)sizeof(tag)) {
- LERROR << "Metadata tag name too long for " << entry.mount_point;
- return -1;
- }
-
- return metadata_find(entry.verity_loc.c_str(), tag, sizeof(struct verity_state), offset);
-}
-
-int load_verity_state(const FstabEntry& entry, int* mode) {
+bool fs_mgr_load_verity_state(int* mode) {
// unless otherwise specified, use EIO mode.
*mode = VERITY_MODE_EIO;
- // use the kernel parameter if set.
- std::string veritymode;
- if (fs_mgr_get_boot_config("veritymode", &veritymode)) {
- if (veritymode == "enforcing") {
- *mode = VERITY_MODE_DEFAULT;
- }
- return 0;
+ // The bootloader communicates verity mode via the kernel commandline
+ std::string verity_mode;
+ if (!fs_mgr_get_boot_config("veritymode", &verity_mode)) {
+ return false;
}
- off64_t offset = 0;
- if (get_verity_state_offset(entry, &offset) < 0) {
- /* fall back to stateless behavior */
- return 0;
- }
-
- if (was_verity_restart()) {
- /* device was restarted after dm-verity detected a corrupted
- * block, so use EIO mode */
- return write_verity_state(entry.verity_loc.c_str(), offset, *mode);
- }
-
- int match = 0;
- if (!compare_last_signature(entry, &match) && !match) {
- /* partition has been reflashed, reset dm-verity state */
+ if (verity_mode == "enforcing") {
*mode = VERITY_MODE_DEFAULT;
- return write_verity_state(entry.verity_loc.c_str(), offset, *mode);
+ } else if (verity_mode == "logging") {
+ *mode = VERITY_MODE_LOGGING;
}
- return read_verity_state(entry.verity_loc.c_str(), offset, mode);
+ return true;
}
// Update the verity table using the actual block device path.
@@ -759,7 +421,7 @@
params.ecc_dev = entry->blk_device.c_str();
- if (load_verity_state(*entry, ¶ms.mode) < 0) {
+ if (!fs_mgr_load_verity_state(¶ms.mode)) {
/* if accessing or updating the state failed, switch to the default
* safe mode. This makes sure the device won't end up in an endless
* restart loop, and no corrupted data will be exposed to userspace
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 88da41d..c7193ab 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -38,7 +38,6 @@
std::string fs_options;
std::string key_loc;
std::string key_dir;
- std::string verity_loc;
off64_t length = 0;
std::string label;
int partnum = -1;
@@ -99,6 +98,7 @@
bool ReadFstabFromFile(const std::string& path, Fstab* fstab);
bool ReadFstabFromDt(Fstab* fstab, bool log = true);
bool ReadDefaultFstab(Fstab* fstab);
+bool SkipMountingPartitions(Fstab* fstab);
FstabEntry* GetEntryForMountPoint(Fstab* fstab, const std::string& path);
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index a4614d0..c2917a4 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -210,6 +210,20 @@
return true;
}
+bool DeviceMapper::GetTargetByName(const std::string& name, DmTargetTypeInfo* info) {
+ std::vector<DmTargetTypeInfo> targets;
+ if (!GetAvailableTargets(&targets)) {
+ return false;
+ }
+ for (const auto& target : targets) {
+ if (target.name() == name) {
+ if (info) *info = target;
+ return true;
+ }
+ }
+ return false;
+}
+
bool DeviceMapper::GetAvailableDevices(std::vector<DmBlockDevice>* devices) {
devices->clear();
diff --git a/fs_mgr/libdm/dm_target.cpp b/fs_mgr/libdm/dm_target.cpp
index cb33eea..f440e6d 100644
--- a/fs_mgr/libdm/dm_target.cpp
+++ b/fs_mgr/libdm/dm_target.cpp
@@ -18,6 +18,7 @@
#include <android-base/logging.h>
#include <android-base/macros.h>
+#include <android-base/parseint.h>
#include <android-base/strings.h>
#include <libdm/dm.h>
@@ -115,5 +116,82 @@
return keyid_ + " " + block_device_;
}
+std::string DmTargetSnapshot::name() const {
+ if (mode_ == SnapshotStorageMode::Merge) {
+ return "snapshot-merge";
+ }
+ return "snapshot";
+}
+
+std::string DmTargetSnapshot::GetParameterString() const {
+ std::string mode;
+ switch (mode_) {
+ case SnapshotStorageMode::Persistent:
+ case SnapshotStorageMode::Merge:
+ // Note: "O" lets us query for overflow in the status message. This
+ // is only supported on kernels 4.4+. On earlier kernels, an overflow
+ // will be reported as "Invalid" in the status string.
+ mode = "P";
+ if (ReportsOverflow(name())) {
+ mode += "O";
+ }
+ break;
+ case SnapshotStorageMode::Transient:
+ mode = "N";
+ break;
+ default:
+ LOG(ERROR) << "DmTargetSnapshot unknown mode";
+ break;
+ }
+ return base_device_ + " " + cow_device_ + " " + mode + " " + std::to_string(chunk_size_);
+}
+
+bool DmTargetSnapshot::ReportsOverflow(const std::string& target_type) {
+ DeviceMapper& dm = DeviceMapper::Instance();
+ DmTargetTypeInfo info;
+ if (!dm.GetTargetByName(target_type, &info)) {
+ return false;
+ }
+ if (target_type == "snapshot") {
+ return info.IsAtLeast(1, 15, 0);
+ }
+ if (target_type == "snapshot-merge") {
+ return info.IsAtLeast(1, 4, 0);
+ }
+ return false;
+}
+
+bool DmTargetSnapshot::ParseStatusText(const std::string& text, Status* status) {
+ auto sections = android::base::Split(text, " ");
+ if (sections.size() == 1) {
+ // This is probably an error code, "Invalid" is possible as is "Overflow"
+ // on 4.4+.
+ status->error = text;
+ return true;
+ }
+ if (sections.size() != 2) {
+ LOG(ERROR) << "snapshot status should have two components";
+ return false;
+ }
+ auto sector_info = android::base::Split(sections[0], "/");
+ if (sector_info.size() != 2) {
+ LOG(ERROR) << "snapshot sector info should have two components";
+ return false;
+ }
+ if (!android::base::ParseUint(sections[1], &status->metadata_sectors)) {
+ LOG(ERROR) << "could not parse metadata sectors";
+ return false;
+ }
+ if (!android::base::ParseUint(sector_info[0], &status->sectors_allocated)) {
+ LOG(ERROR) << "could not parse sectors allocated";
+ return false;
+ }
+ if (!android::base::ParseUint(sector_info[1], &status->total_sectors)) {
+ LOG(ERROR) << "could not parse total sectors";
+ return false;
+ }
+ return true;
+}
+
} // namespace dm
} // namespace android
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
index 70823c6..72a0e11 100644
--- a/fs_mgr/libdm/dm_test.cpp
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -24,6 +24,7 @@
#include <chrono>
#include <ctime>
+#include <iostream>
#include <map>
#include <thread>
@@ -35,21 +36,15 @@
#include "test_util.h"
using namespace std;
+using namespace std::chrono_literals;
using namespace android::dm;
using unique_fd = android::base::unique_fd;
TEST(libdm, HasMinimumTargets) {
+ DmTargetTypeInfo info;
+
DeviceMapper& dm = DeviceMapper::Instance();
- vector<DmTargetTypeInfo> targets;
- ASSERT_TRUE(dm.GetAvailableTargets(&targets));
-
- map<string, DmTargetTypeInfo> by_name;
- for (const auto& target : targets) {
- by_name[target.name()] = target;
- }
-
- auto iter = by_name.find("linear");
- EXPECT_NE(iter, by_name.end());
+ ASSERT_TRUE(dm.GetTargetByName("linear", &info));
}
// Helper to ensure that device mapper devices are released.
@@ -201,3 +196,245 @@
"2 fec_blocks 126955 fec_start 126955 restart_on_corruption ignore_zero_blocks";
EXPECT_EQ(target.GetParameterString(), expected);
}
+
+TEST(libdm, DmSnapshotArgs) {
+ DmTargetSnapshot target1(0, 512, "base", "cow", SnapshotStorageMode::Persistent, 8);
+ if (DmTargetSnapshot::ReportsOverflow("snapshot")) {
+ EXPECT_EQ(target1.GetParameterString(), "base cow PO 8");
+ } else {
+ EXPECT_EQ(target1.GetParameterString(), "base cow P 8");
+ }
+ EXPECT_EQ(target1.name(), "snapshot");
+
+ DmTargetSnapshot target2(0, 512, "base", "cow", SnapshotStorageMode::Transient, 8);
+ EXPECT_EQ(target2.GetParameterString(), "base cow N 8");
+ EXPECT_EQ(target2.name(), "snapshot");
+
+ DmTargetSnapshot target3(0, 512, "base", "cow", SnapshotStorageMode::Merge, 8);
+ if (DmTargetSnapshot::ReportsOverflow("snapshot-merge")) {
+ EXPECT_EQ(target3.GetParameterString(), "base cow PO 8");
+ } else {
+ EXPECT_EQ(target3.GetParameterString(), "base cow P 8");
+ }
+ EXPECT_EQ(target3.name(), "snapshot-merge");
+}
+
+TEST(libdm, DmSnapshotOriginArgs) {
+ DmTargetSnapshotOrigin target(0, 512, "base");
+ EXPECT_EQ(target.GetParameterString(), "base");
+ EXPECT_EQ(target.name(), "snapshot-origin");
+}
+
+class SnapshotTestHarness final {
+ public:
+ bool Setup();
+ bool Merge();
+
+ std::string origin_dev() const { return origin_dev_->path(); }
+ std::string snapshot_dev() const { return snapshot_dev_->path(); }
+
+ int base_fd() const { return base_fd_; }
+
+ static const uint64_t kBaseDeviceSize = 1024 * 1024;
+ static const uint64_t kCowDeviceSize = 1024 * 64;
+ static const uint64_t kSectorSize = 512;
+
+ private:
+ void SetupImpl();
+ void MergeImpl();
+
+ unique_fd base_fd_;
+ unique_fd cow_fd_;
+ unique_ptr<LoopDevice> base_loop_;
+ unique_ptr<LoopDevice> cow_loop_;
+ unique_ptr<TempDevice> origin_dev_;
+ unique_ptr<TempDevice> snapshot_dev_;
+ bool setup_ok_ = false;
+ bool merge_ok_ = false;
+};
+
+bool SnapshotTestHarness::Setup() {
+ SetupImpl();
+ return setup_ok_;
+}
+
+void SnapshotTestHarness::SetupImpl() {
+ base_fd_ = CreateTempFile("base_device", kBaseDeviceSize);
+ ASSERT_GE(base_fd_, 0);
+ cow_fd_ = CreateTempFile("cow_device", kCowDeviceSize);
+ ASSERT_GE(cow_fd_, 0);
+
+ base_loop_ = std::make_unique<LoopDevice>(base_fd_);
+ ASSERT_TRUE(base_loop_->valid());
+ cow_loop_ = std::make_unique<LoopDevice>(cow_fd_);
+ ASSERT_TRUE(cow_loop_->valid());
+
+ DmTable origin_table;
+ ASSERT_TRUE(origin_table.AddTarget(make_unique<DmTargetSnapshotOrigin>(
+ 0, kBaseDeviceSize / kSectorSize, base_loop_->device())));
+ ASSERT_TRUE(origin_table.valid());
+
+ origin_dev_ = std::make_unique<TempDevice>("libdm-test-dm-snapshot-origin", origin_table);
+ ASSERT_TRUE(origin_dev_->valid());
+ ASSERT_FALSE(origin_dev_->path().empty());
+ ASSERT_TRUE(origin_dev_->WaitForUdev());
+
+ // chunk size = 4K blocks.
+ DmTable snap_table;
+ ASSERT_TRUE(snap_table.AddTarget(make_unique<DmTargetSnapshot>(
+ 0, kBaseDeviceSize / kSectorSize, base_loop_->device(), cow_loop_->device(),
+ SnapshotStorageMode::Persistent, 8)));
+ ASSERT_TRUE(snap_table.valid());
+
+ snapshot_dev_ = std::make_unique<TempDevice>("libdm-test-dm-snapshot", snap_table);
+ ASSERT_TRUE(snapshot_dev_->valid());
+ ASSERT_FALSE(snapshot_dev_->path().empty());
+ ASSERT_TRUE(snapshot_dev_->WaitForUdev());
+
+ setup_ok_ = true;
+}
+
+bool SnapshotTestHarness::Merge() {
+ MergeImpl();
+ return merge_ok_;
+}
+
+void SnapshotTestHarness::MergeImpl() {
+ DmTable merge_table;
+ ASSERT_TRUE(merge_table.AddTarget(
+ make_unique<DmTargetSnapshot>(0, kBaseDeviceSize / kSectorSize, base_loop_->device(),
+ cow_loop_->device(), SnapshotStorageMode::Merge, 8)));
+ ASSERT_TRUE(merge_table.valid());
+
+ DeviceMapper& dm = DeviceMapper::Instance();
+ ASSERT_TRUE(dm.LoadTableAndActivate("libdm-test-dm-snapshot", merge_table));
+
+ while (true) {
+ vector<DeviceMapper::TargetInfo> status;
+ ASSERT_TRUE(dm.GetTableStatus("libdm-test-dm-snapshot", &status));
+ ASSERT_EQ(status.size(), 1);
+ ASSERT_EQ(strncmp(status[0].spec.target_type, "snapshot-merge", strlen("snapshot-merge")),
+ 0);
+
+ DmTargetSnapshot::Status merge_status;
+ ASSERT_TRUE(DmTargetSnapshot::ParseStatusText(status[0].data, &merge_status));
+ ASSERT_TRUE(merge_status.error.empty());
+ if (merge_status.sectors_allocated == merge_status.metadata_sectors) {
+ break;
+ }
+
+ std::this_thread::sleep_for(250ms);
+ }
+
+ merge_ok_ = true;
+}
+
+bool CheckSnapshotAvailability() {
+ DmTargetTypeInfo info;
+
+ DeviceMapper& dm = DeviceMapper::Instance();
+ if (!dm.GetTargetByName("snapshot", &info)) {
+ cout << "snapshot module not enabled; skipping test" << std::endl;
+ return false;
+ }
+ if (!dm.GetTargetByName("snapshot-merge", &info)) {
+ cout << "snapshot-merge module not enabled; skipping test" << std::endl;
+ return false;
+ }
+ if (!dm.GetTargetByName("snapshot-origin", &info)) {
+ cout << "snapshot-origin module not enabled; skipping test" << std::endl;
+ return false;
+ }
+ return true;
+}
+
+TEST(libdm, DmSnapshot) {
+ if (!CheckSnapshotAvailability()) {
+ return;
+ }
+
+ SnapshotTestHarness harness;
+ ASSERT_TRUE(harness.Setup());
+
+ // Open the dm devices.
+ unique_fd origin_fd(open(harness.origin_dev().c_str(), O_RDONLY | O_CLOEXEC));
+ ASSERT_GE(origin_fd, 0);
+ unique_fd snapshot_fd(open(harness.snapshot_dev().c_str(), O_RDWR | O_CLOEXEC | O_SYNC));
+ ASSERT_GE(snapshot_fd, 0);
+
+ // Write to the first block of the snapshot device.
+ std::string data("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
+ ASSERT_TRUE(android::base::WriteFully(snapshot_fd, data.data(), data.size()));
+ ASSERT_EQ(lseek(snapshot_fd, 0, SEEK_SET), 0);
+
+ // We should get the same data back from the snapshot device.
+ std::string read(data.size(), '\0');
+ ASSERT_TRUE(android::base::ReadFully(snapshot_fd, read.data(), read.size()));
+ ASSERT_EQ(read, data);
+
+ // We should see the original data from the origin device.
+ std::string zeroes(data.size(), '\0');
+ ASSERT_TRUE(android::base::ReadFully(origin_fd, read.data(), read.size()));
+ ASSERT_EQ(lseek(snapshot_fd, 0, SEEK_SET), 0);
+ ASSERT_EQ(read, zeroes);
+
+ // We should also see the original data from the base device.
+ ASSERT_TRUE(android::base::ReadFully(harness.base_fd(), read.data(), read.size()));
+ ASSERT_EQ(lseek(harness.base_fd(), 0, SEEK_SET), 0);
+ ASSERT_EQ(read, zeroes);
+
+ // Now, perform the merge and wait.
+ ASSERT_TRUE(harness.Merge());
+
+ // Reading from the base device should give us the modified data.
+ ASSERT_TRUE(android::base::ReadFully(harness.base_fd(), read.data(), read.size()));
+ ASSERT_EQ(lseek(harness.base_fd(), 0, SEEK_SET), 0);
+ ASSERT_EQ(read, data);
+}
+
+TEST(libdm, DmSnapshotOverflow) {
+ if (!CheckSnapshotAvailability()) {
+ return;
+ }
+
+ SnapshotTestHarness harness;
+ ASSERT_TRUE(harness.Setup());
+
+ // Open the dm devices.
+ unique_fd snapshot_fd(open(harness.snapshot_dev().c_str(), O_RDWR | O_CLOEXEC));
+ ASSERT_GE(snapshot_fd, 0);
+
+ // Fill the copy-on-write device until it overflows.
+ uint64_t bytes_remaining = SnapshotTestHarness::kCowDeviceSize;
+ uint8_t byte = 1;
+ while (bytes_remaining) {
+ std::string data(4096, char(byte));
+ if (!android::base::WriteFully(snapshot_fd, data.data(), data.size())) {
+ ASSERT_EQ(errno, EIO);
+ break;
+ }
+ bytes_remaining -= data.size();
+ }
+
+ // If writes succeed (because they are buffered), then we should expect an
+ // fsync to fail with EIO.
+ if (!bytes_remaining) {
+ ASSERT_EQ(fsync(snapshot_fd), -1);
+ ASSERT_EQ(errno, EIO);
+ }
+
+ DeviceMapper& dm = DeviceMapper::Instance();
+
+ vector<DeviceMapper::TargetInfo> target_status;
+ ASSERT_TRUE(dm.GetTableStatus("libdm-test-dm-snapshot", &target_status));
+ ASSERT_EQ(target_status.size(), 1);
+ ASSERT_EQ(strncmp(target_status[0].spec.target_type, "snapshot", strlen("snapshot")), 0);
+
+ DmTargetSnapshot::Status status;
+ ASSERT_TRUE(DmTargetSnapshot::ParseStatusText(target_status[0].data, &status));
+ if (DmTargetSnapshot::ReportsOverflow("snapshot")) {
+ ASSERT_EQ(status.error, "Overflow");
+ } else {
+ ASSERT_EQ(status.error, "Invalid");
+ }
+}
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index 28e6e01..d7e8aa9 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -96,6 +96,10 @@
// successfully read and stored in 'targets'. Returns 'false' otherwise.
bool GetAvailableTargets(std::vector<DmTargetTypeInfo>* targets);
+ // Finds a target by name and returns its information if found. |info| may
+ // be null to check for the existence of a target.
+ bool GetTargetByName(const std::string& name, DmTargetTypeInfo* info);
+
// Return 'true' if it can successfully read the list of device mapper block devices
// currently created. 'devices' will be empty if the kernel interactions
// were successful and there are no block devices at the moment. Returns
diff --git a/fs_mgr/libdm/include/libdm/dm_target.h b/fs_mgr/libdm/include/libdm/dm_target.h
index 175b0f0..fce1175 100644
--- a/fs_mgr/libdm/include/libdm/dm_target.h
+++ b/fs_mgr/libdm/include/libdm/dm_target.h
@@ -40,6 +40,18 @@
return std::to_string(major_) + "." + std::to_string(minor_) + "." + std::to_string(patch_);
}
+ uint32_t major_version() const { return major_; }
+ uint32_t minor_version() const { return minor_; }
+ uint32_t patch_level() const { return patch_; }
+
+ bool IsAtLeast(uint32_t major, uint32_t minor, uint32_t patch) const {
+ if (major_ > major) return true;
+ if (major_ < major) return false;
+ if (minor_ > minor) return true;
+ if (minor_ < minor) return false;
+ return patch_ >= patch;
+ }
+
private:
std::string name_;
uint32_t major_;
@@ -170,6 +182,65 @@
std::string target_string_;
};
+enum class SnapshotStorageMode {
+ // The snapshot will be persisted to the COW device.
+ Persistent,
+ // The snapshot will be lost on reboot.
+ Transient,
+ // The snapshot will be merged from the COW device into the base device,
+ // in the background.
+ Merge
+};
+
+// Writes to a snapshot device will be written to the given COW device. Reads
+// will read from the COW device or base device. The chunk size is specified
+// in sectors.
+class DmTargetSnapshot final : public DmTarget {
+ public:
+ DmTargetSnapshot(uint64_t start, uint64_t length, const std::string& base_device,
+ const std::string& cow_device, SnapshotStorageMode mode, uint64_t chunk_size)
+ : DmTarget(start, length),
+ base_device_(base_device),
+ cow_device_(cow_device),
+ mode_(mode),
+ chunk_size_(chunk_size) {}
+
+ std::string name() const override;
+ std::string GetParameterString() const override;
+ bool Valid() const override { return true; }
+
+ struct Status {
+ uint64_t sectors_allocated;
+ uint64_t total_sectors;
+ uint64_t metadata_sectors;
+ std::string error;
+ };
+
+ static bool ParseStatusText(const std::string& text, Status* status);
+ static bool ReportsOverflow(const std::string& target_type);
+
+ private:
+ std::string base_device_;
+ std::string cow_device_;
+ SnapshotStorageMode mode_;
+ uint64_t chunk_size_;
+};
+
+// snapshot-origin will read/write directly to the backing device, updating any
+// snapshot devices with a matching origin.
+class DmTargetSnapshotOrigin final : public DmTarget {
+ public:
+ DmTargetSnapshotOrigin(uint64_t start, uint64_t length, const std::string& device)
+ : DmTarget(start, length), device_(device) {}
+
+ std::string name() const override { return "snapshot-origin"; }
+ std::string GetParameterString() const override { return device_; }
+ bool Valid() const override { return true; }
+
+ private:
+ std::string device_;
+};
+
} // namespace dm
} // namespace android
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
index 8fc02cb..fcef1f0 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -21,6 +21,8 @@
#include <android-base/file.h>
#include <android-base/unique_fd.h>
+#include <fs_mgr.h>
+#include <fstab/fstab.h>
#include <gtest/gtest.h>
#include <liblp/builder.h>
@@ -702,3 +704,19 @@
ASSERT_EQ(updated->block_devices.size(), static_cast<size_t>(1));
EXPECT_EQ(GetBlockDevicePartitionName(updated->block_devices[0]), "super");
}
+
+TEST(liblp, ReadSuperPartition) {
+ auto slot_suffix = fs_mgr_get_slot_suffix();
+ auto slot_number = SlotNumberForSlotSuffix(slot_suffix);
+ auto super_name = fs_mgr_get_super_partition_name(slot_number);
+ auto metadata = ReadMetadata(super_name, slot_number);
+ ASSERT_NE(metadata, nullptr);
+
+ if (!slot_suffix.empty()) {
+ auto other_slot_suffix = fs_mgr_get_other_slot_suffix();
+ auto other_slot_number = SlotNumberForSlotSuffix(other_slot_suffix);
+ auto other_super_name = fs_mgr_get_super_partition_name(other_slot_number);
+ auto other_metadata = ReadMetadata(other_super_name, other_slot_number);
+ ASSERT_NE(other_metadata, nullptr);
+ }
+}
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index 72afa69..6d87594 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -394,7 +394,7 @@
std::string fstab_contents = R"fs(
source none0 swap defaults encryptable,forceencrypt,fileencryption,forcefdeorfbe,keydirectory,length,swapprio,zramsize,max_comp_streams,reservedsize,eraseblk,logicalblk,sysfs_path,zram_loopback_path,zram_loopback_size,zram_backing_dev_path
-source none1 swap defaults encryptable=,forceencrypt=,fileencryption=,keydirectory=,length=,swapprio=,zramsize=,max_comp_streams=,verify=,avb=,reservedsize=,eraseblk=,logicalblk=,sysfs_path=,zram_loopback_path=,zram_loopback_size=,zram_backing_dev_path=
+source none1 swap defaults encryptable=,forceencrypt=,fileencryption=,keydirectory=,length=,swapprio=,zramsize=,max_comp_streams=,avb=,reservedsize=,eraseblk=,logicalblk=,sysfs_path=,zram_loopback_path=,zram_loopback_size=,zram_backing_dev_path=
source none2 swap defaults forcefdeorfbe=
@@ -413,7 +413,6 @@
}
EXPECT_EQ("", entry->key_loc);
EXPECT_EQ("", entry->key_dir);
- EXPECT_EQ("", entry->verity_loc);
EXPECT_EQ(0, entry->length);
EXPECT_EQ("", entry->label);
EXPECT_EQ(-1, entry->partnum);
@@ -437,13 +436,11 @@
flags.crypt = true;
flags.force_crypt = true;
flags.file_encryption = true;
- flags.verify = true;
flags.avb = true;
EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
}
EXPECT_EQ("", entry->key_loc);
EXPECT_EQ("", entry->key_dir);
- EXPECT_EQ("", entry->verity_loc);
EXPECT_EQ(0, entry->length);
EXPECT_EQ("", entry->label);
EXPECT_EQ(-1, entry->partnum);
@@ -639,29 +636,6 @@
EXPECT_EQ(0, entry->zram_size);
}
-TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_Verify) {
- TemporaryFile tf;
- ASSERT_TRUE(tf.fd != -1);
- std::string fstab_contents = R"fs(
-source none0 swap defaults verify=/dir/key
-)fs";
-
- ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
-
- Fstab fstab;
- EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
- ASSERT_EQ(1U, fstab.size());
-
- auto entry = fstab.begin();
- EXPECT_EQ("none0", entry->mount_point);
-
- FstabEntry::FsMgrFlags flags = {};
- flags.verify = true;
- EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
-
- EXPECT_EQ("/dir/key", entry->verity_loc);
-}
-
TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_ForceEncrypt) {
TemporaryFile tf;
ASSERT_TRUE(tf.fd != -1);
diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp
index 9309aad..7e6ad5b 100644
--- a/fs_mgr/tools/dmctl.cpp
+++ b/fs_mgr/tools/dmctl.cpp
@@ -38,15 +38,7 @@
#include <vector>
using namespace std::literals::string_literals;
-
-using DeviceMapper = ::android::dm::DeviceMapper;
-using DmTable = ::android::dm::DmTable;
-using DmTarget = ::android::dm::DmTarget;
-using DmTargetLinear = ::android::dm::DmTargetLinear;
-using DmTargetZero = ::android::dm::DmTargetZero;
-using DmTargetAndroidVerity = ::android::dm::DmTargetAndroidVerity;
-using DmTargetBow = ::android::dm::DmTargetBow;
-using DmTargetTypeInfo = ::android::dm::DmTargetTypeInfo;
+using namespace android::dm;
using DmBlockDevice = ::android::dm::DeviceMapper::DmBlockDevice;
static int Usage(void) {
@@ -57,6 +49,7 @@
std::cerr << " delete <dm-name>" << std::endl;
std::cerr << " list <devices | targets> [-v]" << std::endl;
std::cerr << " getpath <dm-name>" << std::endl;
+ std::cerr << " status <dm-name>" << std::endl;
std::cerr << " table <dm-name>" << std::endl;
std::cerr << " help" << std::endl;
std::cerr << std::endl;
@@ -122,6 +115,62 @@
}
std::string block_device = NextArg();
return std::make_unique<DmTargetBow>(start_sector, num_sectors, block_device);
+ } else if (target_type == "snapshot-origin") {
+ if (!HasArgs(1)) {
+ std::cerr << "Expected \"snapshot-origin\" <block_device>" << std::endl;
+ return nullptr;
+ }
+ std::string block_device = NextArg();
+ return std::make_unique<DmTargetSnapshotOrigin>(start_sector, num_sectors,
+ block_device);
+ } else if (target_type == "snapshot") {
+ if (!HasArgs(4)) {
+ std::cerr
+ << "Expected \"snapshot\" <block_device> <block_device> <mode> <chunk_size>"
+ << std::endl;
+ return nullptr;
+ }
+ std::string base_device = NextArg();
+ std::string cow_device = NextArg();
+ std::string mode_str = NextArg();
+ std::string chunk_size_str = NextArg();
+
+ SnapshotStorageMode mode;
+ if (mode_str == "P") {
+ mode = SnapshotStorageMode::Persistent;
+ } else if (mode_str == "N") {
+ mode = SnapshotStorageMode::Transient;
+ } else {
+ std::cerr << "Unrecognized mode: " << mode_str << "\n";
+ return nullptr;
+ }
+
+ uint32_t chunk_size;
+ if (!android::base::ParseUint(chunk_size_str, &chunk_size)) {
+ std::cerr << "Chunk size must be an unsigned integer.\n";
+ return nullptr;
+ }
+ return std::make_unique<DmTargetSnapshot>(start_sector, num_sectors, base_device,
+ cow_device, mode, chunk_size);
+ } else if (target_type == "snapshot-merge") {
+ if (!HasArgs(3)) {
+ std::cerr
+ << "Expected \"snapshot-merge\" <block_device> <block_device> <chunk_size>"
+ << std::endl;
+ return nullptr;
+ }
+ std::string base_device = NextArg();
+ std::string cow_device = NextArg();
+ std::string chunk_size_str = NextArg();
+ SnapshotStorageMode mode = SnapshotStorageMode::Merge;
+
+ uint32_t chunk_size;
+ if (!android::base::ParseUint(chunk_size_str, &chunk_size)) {
+ std::cerr << "Chunk size must be an unsigned integer.\n";
+ return nullptr;
+ }
+ return std::make_unique<DmTargetSnapshot>(start_sector, num_sectors, base_device,
+ cow_device, mode, chunk_size);
} else {
std::cerr << "Unrecognized target type: " << target_type << std::endl;
return nullptr;
@@ -308,7 +357,7 @@
return 0;
}
-static int TableCmdHandler(int argc, char** argv) {
+static int DumpTable(const std::string& mode, int argc, char** argv) {
if (argc != 1) {
std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
return -EINVAL;
@@ -316,9 +365,18 @@
DeviceMapper& dm = DeviceMapper::Instance();
std::vector<DeviceMapper::TargetInfo> table;
- if (!dm.GetTableInfo(argv[0], &table)) {
- std::cerr << "Could not query table status of device \"" << argv[0] << "\"." << std::endl;
- return -EINVAL;
+ if (mode == "status") {
+ if (!dm.GetTableStatus(argv[0], &table)) {
+ std::cerr << "Could not query table status of device \"" << argv[0] << "\"."
+ << std::endl;
+ return -EINVAL;
+ }
+ } else if (mode == "table") {
+ if (!dm.GetTableInfo(argv[0], &table)) {
+ std::cerr << "Could not query table status of device \"" << argv[0] << "\"."
+ << std::endl;
+ return -EINVAL;
+ }
}
std::cout << "Targets in the device-mapper table for " << argv[0] << ":" << std::endl;
for (const auto& target : table) {
@@ -333,6 +391,14 @@
return 0;
}
+static int TableCmdHandler(int argc, char** argv) {
+ return DumpTable("table", argc, argv);
+}
+
+static int StatusCmdHandler(int argc, char** argv) {
+ return DumpTable("status", argc, argv);
+}
+
static std::map<std::string, std::function<int(int, char**)>> cmdmap = {
// clang-format off
{"create", DmCreateCmdHandler},
@@ -341,6 +407,7 @@
{"help", HelpCmdHandler},
{"getpath", GetPathCmdHandler},
{"table", TableCmdHandler},
+ {"status", StatusCmdHandler},
// clang-format on
};
diff --git a/init/README.md b/init/README.md
index 51deb5a..79c6237 100644
--- a/init/README.md
+++ b/init/README.md
@@ -191,7 +191,7 @@
`critical`
> This is a device-critical service. If it exits more than four times in
- four minutes, the device will reboot into bootloader.
+ four minutes or before boot completes, the device will reboot into bootloader.
`disabled`
> This service will not automatically start with its class.
@@ -412,6 +412,10 @@
not already running. See the start entry for more information on
starting services.
+`class_start_post_data <serviceclass>`
+> Like `class_start`, but only considers services that were started
+ after /data was mounted. Only used for FDE devices.
+
`class_stop <serviceclass>`
> Stop and disable all services of the specified class if they are
currently running.
@@ -421,6 +425,10 @@
currently running, without disabling them. They can be restarted
later using `class_start`.
+`class_reset_post_data <serviceclass>`
+> Like `class_reset`, but only considers services that were started
+ after /data was mounted. Only used for FDE devices.
+
`class_restart <serviceclass>`
> Restarts all services of the specified class.
@@ -494,6 +502,10 @@
`write` command to write to `/proc/sys/kernel/printk` to change that.
Properties are expanded within _level_.
+`mark_post_data`
+> Used to mark the point right after /data is mounted. Used to implement the
+ `class_reset_post_data` and `class_start_post_data` commands.
+
`mkdir <path> [mode] [owner] [group]`
> Create a directory at _path_, optionally with the given mode, owner, and
group. If not provided, the directory is created with permissions 755 and
@@ -586,9 +598,6 @@
`umount <path>`
> Unmount the filesystem mounted at that path.
-`verity_load_state`
-> Internal implementation detail used to load dm-verity state.
-
`verity_update_state <mount-point>`
> Internal implementation detail used to update dm-verity state and
set the partition._mount-point_.verified properties used by adb remount
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 06da4be..ba1c94d 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -104,23 +104,37 @@
}
}
-static Result<Success> do_class_start(const BuiltinArguments& args) {
+static Result<Success> class_start(const std::string& class_name, bool post_data_only) {
// Do not start a class if it has a property persist.dont_start_class.CLASS set to 1.
- if (android::base::GetBoolProperty("persist.init.dont_start_class." + args[1], false))
+ if (android::base::GetBoolProperty("persist.init.dont_start_class." + class_name, false))
return Success();
// Starting a class does not start services which are explicitly disabled.
// They must be started individually.
for (const auto& service : ServiceList::GetInstance()) {
- if (service->classnames().count(args[1])) {
+ if (service->classnames().count(class_name)) {
+ if (post_data_only && !service->is_post_data()) {
+ continue;
+ }
if (auto result = service->StartIfNotDisabled(); !result) {
LOG(ERROR) << "Could not start service '" << service->name()
- << "' as part of class '" << args[1] << "': " << result.error();
+ << "' as part of class '" << class_name << "': " << result.error();
}
}
}
return Success();
}
+static Result<Success> do_class_start(const BuiltinArguments& args) {
+ return class_start(args[1], false /* post_data_only */);
+}
+
+static Result<Success> do_class_start_post_data(const BuiltinArguments& args) {
+ if (args.context != kInitContext) {
+ return Error() << "command 'class_start_post_data' only available in init context";
+ }
+ return class_start(args[1], true /* post_data_only */);
+}
+
static Result<Success> do_class_stop(const BuiltinArguments& args) {
ForEachServiceInClass(args[1], &Service::Stop);
return Success();
@@ -131,6 +145,14 @@
return Success();
}
+static Result<Success> do_class_reset_post_data(const BuiltinArguments& args) {
+ if (args.context != kInitContext) {
+ return Error() << "command 'class_reset_post_data' only available in init context";
+ }
+ ForEachServiceInClass(args[1], &Service::ResetIfPostData);
+ return Success();
+}
+
static Result<Success> do_class_restart(const BuiltinArguments& args) {
// Do not restart a class if it has a property persist.dont_start_class.CLASS set to 1.
if (android::base::GetBoolProperty("persist.init.dont_start_class." + args[1], false))
@@ -707,17 +729,6 @@
return Success();
}
-static Result<Success> do_verity_load_state(const BuiltinArguments& args) {
- int mode = -1;
- bool loaded = fs_mgr_load_verity_state(&mode);
- if (loaded && mode != VERITY_MODE_DEFAULT) {
- ActionManager::GetInstance().QueueEventTrigger("verity-logging");
- }
- if (!loaded) return Error() << "Could not load verity state";
-
- return Success();
-}
-
static Result<Success> do_verity_update_state(const BuiltinArguments& args) {
int mode;
if (!fs_mgr_load_verity_state(&mode)) {
@@ -1053,6 +1064,12 @@
{{"exec", "/system/bin/vdc", "--wait", "cryptfs", "init_user0"}, args.context});
}
+static Result<Success> do_mark_post_data(const BuiltinArguments& args) {
+ ServiceList::GetInstance().MarkPostData();
+
+ return Success();
+}
+
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
@@ -1104,8 +1121,10 @@
{"chmod", {2, 2, {true, do_chmod}}},
{"chown", {2, 3, {true, do_chown}}},
{"class_reset", {1, 1, {false, do_class_reset}}},
+ {"class_reset_post_data", {1, 1, {false, do_class_reset_post_data}}},
{"class_restart", {1, 1, {false, do_class_restart}}},
{"class_start", {1, 1, {false, do_class_start}}},
+ {"class_start_post_data", {1, 1, {false, do_class_start_post_data}}},
{"class_stop", {1, 1, {false, do_class_stop}}},
{"copy", {2, 2, {true, do_copy}}},
{"domainname", {1, 1, {true, do_domainname}}},
@@ -1125,6 +1144,7 @@
{"load_persist_props", {0, 0, {false, do_load_persist_props}}},
{"load_system_props", {0, 0, {false, do_load_system_props}}},
{"loglevel", {1, 1, {false, do_loglevel}}},
+ {"mark_post_data", {0, 0, {false, do_mark_post_data}}},
{"mkdir", {1, 4, {true, do_mkdir}}},
// TODO: Do mount operations in vendor_init.
// mount_all is currently too complex to run in vendor_init as it queues action triggers,
@@ -1150,7 +1170,6 @@
{"symlink", {2, 2, {true, do_symlink}}},
{"sysclktz", {1, 1, {false, do_sysclktz}}},
{"trigger", {1, 1, {false, do_trigger}}},
- {"verity_load_state", {0, 0, {false, do_verity_load_state}}},
{"verity_update_state", {0, 0, {false, do_verity_update_state}}},
{"wait", {1, 2, {true, do_wait}}},
{"wait_for_prop", {2, 2, {false, do_wait_for_prop}}},
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 3900f72..85fa874 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -43,7 +43,6 @@
#include "uevent_listener.h"
#include "util.h"
-using android::base::ReadFileToString;
using android::base::Split;
using android::base::Timer;
using android::fs_mgr::AvbHandle;
@@ -55,6 +54,7 @@
using android::fs_mgr::FstabEntry;
using android::fs_mgr::ReadDefaultFstab;
using android::fs_mgr::ReadFstabFromDt;
+using android::fs_mgr::SkipMountingPartitions;
using namespace std::literals;
@@ -524,38 +524,10 @@
return true;
}
-// For GSI to skip mounting /product and /product_services, until there are
-// well-defined interfaces between them and /system. Otherwise, the GSI flashed
-// on /system might not be able to work with /product and /product_services.
-// When they're skipped here, /system/product and /system/product_services in
-// GSI will be used.
-bool FirstStageMount::TrySkipMountingPartitions() {
- constexpr const char kSkipMountConfig[] = "/system/etc/init/config/skip_mount.cfg";
-
- std::string skip_config;
- if (!ReadFileToString(kSkipMountConfig, &skip_config)) {
- return true;
- }
-
- for (const auto& skip_mount_point : Split(skip_config, "\n")) {
- if (skip_mount_point.empty()) {
- continue;
- }
- auto it = std::remove_if(fstab_.begin(), fstab_.end(),
- [&skip_mount_point](const auto& entry) {
- return entry.mount_point == skip_mount_point;
- });
- fstab_.erase(it, fstab_.end());
- LOG(INFO) << "Skip mounting partition: " << skip_mount_point;
- }
-
- return true;
-}
-
bool FirstStageMount::MountPartitions() {
if (!TrySwitchSystemAsRoot()) return false;
- if (!TrySkipMountingPartitions()) return false;
+ if (!SkipMountingPartitions(&fstab_)) return false;
for (auto current = fstab_.begin(); current != fstab_.end();) {
// We've already mounted /system above.
@@ -672,7 +644,6 @@
}
bool FirstStageMountVBootV1::GetDmVerityDevices() {
- std::string verity_loc_device;
need_dm_verity_ = false;
for (const auto& fstab_entry : fstab_) {
@@ -685,21 +656,9 @@
if (fstab_entry.fs_mgr_flags.verify) {
need_dm_verity_ = true;
}
- // Checks if verity metadata is on a separate partition. Note that it is
- // not partition specific, so there must be only one additional partition
- // that carries verity state.
- if (!fstab_entry.verity_loc.empty()) {
- if (verity_loc_device.empty()) {
- verity_loc_device = fstab_entry.verity_loc;
- } else if (verity_loc_device != fstab_entry.verity_loc) {
- LOG(ERROR) << "More than one verity_loc found: " << verity_loc_device << ", "
- << fstab_entry.verity_loc;
- return false;
- }
- }
}
- // Includes the partition names of fstab records and verity_loc_device (if any).
+ // Includes the partition names of fstab records.
// Notes that fstab_rec->blk_device has A/B suffix updated by fs_mgr when A/B is used.
for (const auto& fstab_entry : fstab_) {
if (!fstab_entry.fs_mgr_flags.logical) {
@@ -707,10 +666,6 @@
}
}
- if (!verity_loc_device.empty()) {
- required_devices_partition_names_.emplace(basename(verity_loc_device.c_str()));
- }
-
return true;
}
diff --git a/init/property_service.cpp b/init/property_service.cpp
index bf3b317..a1e9551 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -41,7 +41,9 @@
#include <map>
#include <memory>
+#include <mutex>
#include <queue>
+#include <thread>
#include <vector>
#include <android-base/chrono_utils.h>
@@ -83,6 +85,8 @@
namespace android {
namespace init {
+static constexpr const char kRestoreconProperty[] = "selinux.restorecon_recursive";
+
static bool persistent_properties_loaded = false;
static int property_set_fd = -1;
@@ -100,7 +104,24 @@
const char* name;
};
+static int PropertyAuditCallback(void* data, security_class_t /*cls*/, char* buf, size_t len) {
+ auto* d = reinterpret_cast<PropertyAuditData*>(data);
+
+ if (!d || !d->name || !d->cr) {
+ LOG(ERROR) << "AuditCallback invoked with null data arguments!";
+ return 0;
+ }
+
+ snprintf(buf, len, "property=%s pid=%d uid=%d gid=%d", d->name, d->cr->pid, d->cr->uid,
+ d->cr->gid);
+ return 0;
+}
+
void property_init() {
+ selinux_callback cb;
+ cb.func_audit = PropertyAuditCallback;
+ selinux_set_callback(SELINUX_CB_AUDIT, cb);
+
mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
CreateSerializedPropertyInfo();
if (__system_property_area_init()) {
@@ -187,88 +208,51 @@
return PROP_SUCCESS;
}
-typedef int (*PropertyAsyncFunc)(const std::string&, const std::string&);
+class AsyncRestorecon {
+ public:
+ void TriggerRestorecon(const std::string& path) {
+ auto guard = std::lock_guard{mutex_};
+ paths_.emplace(path);
-struct PropertyChildInfo {
- pid_t pid;
- PropertyAsyncFunc func;
- std::string name;
- std::string value;
+ if (!thread_started_) {
+ thread_started_ = true;
+ std::thread{&AsyncRestorecon::ThreadFunction, this}.detach();
+ }
+ }
+
+ private:
+ void ThreadFunction() {
+ auto lock = std::unique_lock{mutex_};
+
+ while (!paths_.empty()) {
+ auto path = paths_.front();
+ paths_.pop();
+
+ lock.unlock();
+ if (selinux_android_restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
+ LOG(ERROR) << "Asynchronous restorecon of '" << path << "' failed'";
+ }
+ android::base::SetProperty(kRestoreconProperty, path);
+ lock.lock();
+ }
+
+ thread_started_ = false;
+ }
+
+ std::mutex mutex_;
+ std::queue<std::string> paths_;
+ bool thread_started_ = false;
};
-static std::queue<PropertyChildInfo> property_children;
-
-static void PropertyChildLaunch() {
- auto& info = property_children.front();
- pid_t pid = fork();
- if (pid < 0) {
- LOG(ERROR) << "Failed to fork for property_set_async";
- while (!property_children.empty()) {
- property_children.pop();
- }
- return;
- }
- if (pid != 0) {
- info.pid = pid;
- } else {
- if (info.func(info.name, info.value) != 0) {
- LOG(ERROR) << "property_set_async(\"" << info.name << "\", \"" << info.value
- << "\") failed";
- }
- _exit(0);
- }
-}
-
-bool PropertyChildReap(pid_t pid) {
- if (property_children.empty()) {
- return false;
- }
- auto& info = property_children.front();
- if (info.pid != pid) {
- return false;
- }
- std::string error;
- if (PropertySet(info.name, info.value, &error) != PROP_SUCCESS) {
- LOG(ERROR) << "Failed to set async property " << info.name << " to " << info.value << ": "
- << error;
- }
- property_children.pop();
- if (!property_children.empty()) {
- PropertyChildLaunch();
- }
- return true;
-}
-
-static uint32_t PropertySetAsync(const std::string& name, const std::string& value,
- PropertyAsyncFunc func, std::string* error) {
- if (value.empty()) {
- return PropertySet(name, value, error);
- }
-
- PropertyChildInfo info;
- info.func = func;
- info.name = name;
- info.value = value;
- property_children.push(info);
- if (property_children.size() == 1) {
- PropertyChildLaunch();
- }
- return PROP_SUCCESS;
-}
-
-static int RestoreconRecursiveAsync(const std::string& name, const std::string& value) {
- return selinux_android_restorecon(value.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE);
-}
-
uint32_t InitPropertySet(const std::string& name, const std::string& value) {
if (StartsWith(name, "ctl.")) {
LOG(ERROR) << "InitPropertySet: Do not set ctl. properties from init; call the Service "
"functions directly";
return PROP_ERROR_INVALID_NAME;
}
- if (name == "selinux.restorecon_recursive") {
- LOG(ERROR) << "InitPropertySet: Do not set selinux.restorecon_recursive from init; use the "
- "restorecon builtin directly";
+ if (name == kRestoreconProperty) {
+ LOG(ERROR) << "InitPropertySet: Do not set '" << kRestoreconProperty
+ << "' from init; use the restorecon builtin directly";
return PROP_ERROR_INVALID_NAME;
}
@@ -329,18 +313,20 @@
return result == sizeof(value);
}
+ bool GetSourceContext(std::string* source_context) const {
+ char* c_source_context = nullptr;
+ if (getpeercon(socket_, &c_source_context) != 0) {
+ return false;
+ }
+ *source_context = c_source_context;
+ freecon(c_source_context);
+ return true;
+ }
+
int socket() { return socket_; }
const ucred& cred() { return cred_; }
- std::string source_context() const {
- char* source_context = nullptr;
- getpeercon(socket_, &source_context);
- std::string result = source_context;
- freecon(source_context);
- return result;
- }
-
private:
bool PollIn(uint32_t* timeout_ms) {
struct pollfd ufds[1];
@@ -506,8 +492,14 @@
<< process_log_string;
}
- if (name == "selinux.restorecon_recursive") {
- return PropertySetAsync(name, value, RestoreconRecursiveAsync, error);
+ // If a process other than init is writing a non-empty value, it means that process is
+ // requesting that init performs a restorecon operation on the path specified by 'value'.
+ // We use a thread to do this restorecon operation to prevent holding up init, as it may take
+ // a long time to complete.
+ if (name == kRestoreconProperty && cr.pid != 1 && !value.empty()) {
+ static AsyncRestorecon async_restorecon;
+ async_restorecon.TriggerRestorecon(value);
+ return PROP_SUCCESS;
}
return PropertySet(name, value, error);
@@ -553,10 +545,15 @@
prop_name[PROP_NAME_MAX-1] = 0;
prop_value[PROP_VALUE_MAX-1] = 0;
+ std::string source_context;
+ if (!socket.GetSourceContext(&source_context)) {
+ PLOG(ERROR) << "Unable to set property '" << prop_name << "': getpeercon() failed";
+ return;
+ }
+
const auto& cr = socket.cred();
std::string error;
- uint32_t result =
- HandlePropertySet(prop_name, prop_value, socket.source_context(), cr, &error);
+ uint32_t result = HandlePropertySet(prop_name, prop_value, source_context, cr, &error);
if (result != PROP_SUCCESS) {
LOG(ERROR) << "Unable to set property '" << prop_name << "' from uid:" << cr.uid
<< " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
@@ -575,9 +572,16 @@
return;
}
+ std::string source_context;
+ if (!socket.GetSourceContext(&source_context)) {
+ PLOG(ERROR) << "Unable to set property '" << name << "': getpeercon() failed";
+ socket.SendUint32(PROP_ERROR_PERMISSION_DENIED);
+ return;
+ }
+
const auto& cr = socket.cred();
std::string error;
- uint32_t result = HandlePropertySet(name, value, socket.source_context(), cr, &error);
+ uint32_t result = HandlePropertySet(name, value, source_context, cr, &error);
if (result != PROP_SUCCESS) {
LOG(ERROR) << "Unable to set property '" << name << "' from uid:" << cr.uid
<< " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
@@ -661,7 +665,7 @@
}
if (StartsWith(key, "ctl.") || key == "sys.powerctl"s ||
- key == "selinux.restorecon_recursive"s) {
+ std::string{key} == kRestoreconProperty) {
LOG(ERROR) << "Ignoring disallowed property '" << key
<< "' with special meaning in prop file '" << filename << "'";
continue;
@@ -906,19 +910,6 @@
update_sys_usb_config();
}
-static int SelinuxAuditCallback(void* data, security_class_t /*cls*/, char* buf, size_t len) {
- auto* d = reinterpret_cast<PropertyAuditData*>(data);
-
- if (!d || !d->name || !d->cr) {
- LOG(ERROR) << "AuditCallback invoked with null data arguments!";
- return 0;
- }
-
- snprintf(buf, len, "property=%s pid=%d uid=%d gid=%d", d->name, d->cr->pid, d->cr->uid,
- d->cr->gid);
- return 0;
-}
-
bool LoadPropertyInfoFromFile(const std::string& filename,
std::vector<PropertyInfoEntry>* property_infos) {
auto file_contents = std::string();
@@ -989,10 +980,6 @@
}
void StartPropertyService(Epoll* epoll) {
- selinux_callback cb;
- cb.func_audit = SelinuxAuditCallback;
- selinux_set_callback(SELINUX_CB_AUDIT, cb);
-
property_set("ro.property_service.version", "2");
property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
diff --git a/init/property_service.h b/init/property_service.h
index 207c03b..7f9f844 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -32,8 +32,6 @@
uint32_t HandlePropertySet(const std::string& name, const std::string& value,
const std::string& source_context, const ucred& cr, std::string* error);
-extern bool PropertyChildReap(pid_t pid);
-
void property_init();
void property_load_boot_defaults(bool load_debug_prop);
void load_persist_props();
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 5b90969..0966b6c 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -19,13 +19,14 @@
#include <dirent.h>
#include <fcntl.h>
#include <linux/fs.h>
-#include <mntent.h>
#include <linux/loop.h>
+#include <mntent.h>
+#include <semaphore.h>
#include <sys/cdefs.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
-#include <sys/swap.h>
#include <sys/stat.h>
+#include <sys/swap.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
@@ -57,11 +58,14 @@
#include "service.h"
#include "sigchld_handler.h"
+#define PROC_SYSRQ "/proc/sysrq-trigger"
+
using android::base::GetBoolProperty;
using android::base::Split;
using android::base::StringPrintf;
using android::base::Timer;
using android::base::unique_fd;
+using android::base::WriteStringToFile;
namespace android {
namespace init {
@@ -207,8 +211,8 @@
}
FindPartitionsToUmount(nullptr, nullptr, true);
// dump current CPU stack traces and uninterruptible tasks
- android::base::WriteStringToFile("l", "/proc/sysrq-trigger");
- android::base::WriteStringToFile("w", "/proc/sysrq-trigger");
+ WriteStringToFile("l", PROC_SYSRQ);
+ WriteStringToFile("w", PROC_SYSRQ);
}
static UmountStat UmountPartitions(std::chrono::milliseconds timeout) {
@@ -248,7 +252,91 @@
}
}
-static void KillAllProcesses() { android::base::WriteStringToFile("i", "/proc/sysrq-trigger"); }
+static void KillAllProcesses() {
+ WriteStringToFile("i", PROC_SYSRQ);
+}
+
+// Create reboot/shutdwon monitor thread
+void RebootMonitorThread(unsigned int cmd, const std::string& rebootTarget, sem_t* reboot_semaphore,
+ std::chrono::milliseconds shutdown_timeout, bool* reboot_monitor_run) {
+ unsigned int remaining_shutdown_time = 0;
+
+ // 30 seconds more than the timeout passed to the thread as there is a final Umount pass
+ // after the timeout is reached.
+ constexpr unsigned int shutdown_watchdog_timeout_default = 30;
+ auto shutdown_watchdog_timeout = android::base::GetUintProperty(
+ "ro.build.shutdown.watchdog.timeout", shutdown_watchdog_timeout_default);
+ remaining_shutdown_time = shutdown_watchdog_timeout + shutdown_timeout.count() / 1000;
+
+ while (*reboot_monitor_run == true) {
+ if (TEMP_FAILURE_RETRY(sem_wait(reboot_semaphore)) == -1) {
+ LOG(ERROR) << "sem_wait failed and exit RebootMonitorThread()";
+ return;
+ }
+
+ timespec shutdown_timeout_timespec;
+ if (clock_gettime(CLOCK_MONOTONIC, &shutdown_timeout_timespec) == -1) {
+ LOG(ERROR) << "clock_gettime() fail! exit RebootMonitorThread()";
+ return;
+ }
+
+ // If there are some remaining shutdown time left from previous round, we use
+ // remaining time here.
+ shutdown_timeout_timespec.tv_sec += remaining_shutdown_time;
+
+ LOG(INFO) << "shutdown_timeout_timespec.tv_sec: " << shutdown_timeout_timespec.tv_sec;
+
+ int sem_return = 0;
+ while ((sem_return = sem_timedwait_monotonic_np(reboot_semaphore,
+ &shutdown_timeout_timespec)) == -1 &&
+ errno == EINTR) {
+ }
+
+ if (sem_return == -1) {
+ LOG(ERROR) << "Reboot thread timed out";
+
+ if (android::base::GetBoolProperty("ro.debuggable", false) == true) {
+ LOG(INFO) << "Try to dump init process call trace:";
+ const char* vdc_argv[] = {"/system/bin/debuggerd", "-b", "1"};
+ int status;
+ android_fork_execvp_ext(arraysize(vdc_argv), (char**)vdc_argv, &status, true,
+ LOG_KLOG, true, nullptr, nullptr, 0);
+
+ LOG(INFO) << "Show stack for all active CPU:";
+ WriteStringToFile("l", PROC_SYSRQ);
+
+ LOG(INFO) << "Show tasks that are in disk sleep(uninterruptable sleep), which are "
+ "like "
+ "blocked in mutex or hardware register access:";
+ WriteStringToFile("w", PROC_SYSRQ);
+ }
+
+ // In shutdown case,notify kernel to sync and umount fs to read-only before shutdown.
+ if (cmd == ANDROID_RB_POWEROFF || cmd == ANDROID_RB_THERMOFF) {
+ WriteStringToFile("s", PROC_SYSRQ);
+
+ WriteStringToFile("u", PROC_SYSRQ);
+
+ RebootSystem(cmd, rebootTarget);
+ }
+
+ LOG(ERROR) << "Trigger crash at last!";
+ WriteStringToFile("c", PROC_SYSRQ);
+ } else {
+ timespec current_time_timespec;
+
+ if (clock_gettime(CLOCK_MONOTONIC, ¤t_time_timespec) == -1) {
+ LOG(ERROR) << "clock_gettime() fail! exit RebootMonitorThread()";
+ return;
+ }
+
+ remaining_shutdown_time =
+ shutdown_timeout_timespec.tv_sec - current_time_timespec.tv_sec;
+
+ LOG(INFO) << "remaining_shutdown_time: " << remaining_shutdown_time;
+ }
+ }
+}
/* Try umounting all emulated file systems R/W block device cfile systems.
* This will just try umount and give it up if it fails.
@@ -259,7 +347,8 @@
*
* return true when umount was successful. false when timed out.
*/
-static UmountStat TryUmountAndFsck(bool runFsck, std::chrono::milliseconds timeout) {
+static UmountStat TryUmountAndFsck(unsigned int cmd, const std::string& rebootTarget, bool runFsck,
+ std::chrono::milliseconds timeout, sem_t* reboot_semaphore) {
Timer t;
std::vector<MountEntry> block_devices;
std::vector<MountEntry> emulated_devices;
@@ -279,11 +368,17 @@
}
if (stat == UMOUNT_STAT_SUCCESS && runFsck) {
+ LOG(INFO) << "Pause reboot monitor thread before fsck";
+ sem_post(reboot_semaphore);
+
// fsck part is excluded from timeout check. It only runs for user initiated shutdown
// and should not affect reboot time.
for (auto& entry : block_devices) {
entry.DoFsck();
}
+
+ LOG(INFO) << "Resume reboot monitor thread after fsck";
+ sem_post(reboot_semaphore);
}
return stat;
}
@@ -311,7 +406,7 @@
}
LOG(INFO) << "swapoff() took " << swap_timer;;
- if (!android::base::WriteStringToFile("1", ZRAM_RESET)) {
+ if (!WriteStringToFile("1", ZRAM_RESET)) {
LOG(ERROR) << "zram_backing_dev: reset (" << backing_dev << ")" << " failed";
return;
}
@@ -369,6 +464,23 @@
}
LOG(INFO) << "Shutdown timeout: " << shutdown_timeout.count() << " ms";
+ sem_t reboot_semaphore;
+ if (sem_init(&reboot_semaphore, false, 0) == -1) {
+ // These should never fail, but if they do, skip the graceful reboot and reboot immediately.
+ LOG(ERROR) << "sem_init() fail and RebootSystem() return!";
+ RebootSystem(cmd, rebootTarget);
+ }
+
+ // Start a thread to monitor init shutdown process
+ LOG(INFO) << "Create reboot monitor thread.";
+ bool reboot_monitor_run = true;
+ std::thread reboot_monitor_thread(&RebootMonitorThread, cmd, rebootTarget, &reboot_semaphore,
+ shutdown_timeout, &reboot_monitor_run);
+ reboot_monitor_thread.detach();
+
+ // Start reboot monitor thread
+ sem_post(&reboot_semaphore);
+
// keep debugging tools until non critical ones are all gone.
const std::set<std::string> kill_after_apps{"tombstoned", "logd", "adbd"};
// watchdogd is a vendor specific component but should be alive to complete shutdown safely.
@@ -497,7 +609,8 @@
// 5. drop caches and disable zram backing device, if exist
KillZramBackingDevice();
- UmountStat stat = TryUmountAndFsck(runFsck, shutdown_timeout - t.duration());
+ UmountStat stat = TryUmountAndFsck(cmd, rebootTarget, runFsck, shutdown_timeout - t.duration(),
+ &reboot_semaphore);
// Follow what linux shutdown is doing: one more sync with little bit delay
{
Timer sync_timer;
@@ -507,6 +620,11 @@
}
if (!is_thermal_shutdown) std::this_thread::sleep_for(100ms);
LogShutdownTime(stat, &t);
+
+ // Send signal to terminate reboot monitor thread.
+ reboot_monitor_run = false;
+ sem_post(&reboot_semaphore);
+
// Reboot regardless of umount status. If umount fails, fsck after reboot will fix it.
RebootSystem(cmd, rebootTarget);
abort();
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 132fc13..c49dc9f 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -331,6 +331,12 @@
}
std::string plat_mapping_file("/system/etc/selinux/mapping/" + vend_plat_vers + ".cil");
+ std::string plat_compat_cil_file("/system/etc/selinux/mapping/" + vend_plat_vers +
+ ".compat.cil");
+ if (access(plat_compat_cil_file.c_str(), F_OK) == -1) {
+ plat_compat_cil_file.clear();
+ }
+
std::string product_policy_cil_file("/product/etc/selinux/product_sepolicy.cil");
if (access(product_policy_cil_file.c_str(), F_OK) == -1) {
product_policy_cil_file.clear();
@@ -376,6 +382,9 @@
};
// clang-format on
+ if (!plat_compat_cil_file.empty()) {
+ compile_args.push_back(plat_compat_cil_file.c_str());
+ }
if (!product_policy_cil_file.empty()) {
compile_args.push_back(product_policy_cil_file.c_str());
}
diff --git a/init/service.cpp b/init/service.cpp
index f5c13b9..2f96681 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -362,7 +362,7 @@
// Oneshot processes go into the disabled state on exit,
// except when manually restarted.
- if ((flags_ & SVC_ONESHOT) && !(flags_ & SVC_RESTART)) {
+ if ((flags_ & SVC_ONESHOT) && !(flags_ & SVC_RESTART) && !(flags_ & SVC_RESET)) {
flags_ |= SVC_DISABLED;
}
@@ -372,16 +372,20 @@
return;
}
- // If we crash > 4 times in 4 minutes, reboot into bootloader or set crashing property
+ // If we crash > 4 times in 4 minutes or before boot_completed,
+ // reboot into bootloader or set crashing property
boot_clock::time_point now = boot_clock::now();
if (((flags_ & SVC_CRITICAL) || !pre_apexd_) && !(flags_ & SVC_RESTART)) {
- if (now < time_crashed_ + 4min) {
+ bool boot_completed = android::base::GetBoolProperty("sys.boot_completed", false);
+ if (now < time_crashed_ + 4min || !boot_completed) {
if (++crash_count_ > 4) {
if (flags_ & SVC_CRITICAL) {
// Aborts into bootloader
- LOG(FATAL) << "critical process '" << name_ << "' exited 4 times in 4 minutes";
+ LOG(FATAL) << "critical process '" << name_ << "' exited 4 times "
+ << (boot_completed ? "in 4 minutes" : "before boot completed");
} else {
- LOG(ERROR) << "updatable process '" << name_ << "' exited 4 times in 4 minutes";
+ LOG(ERROR) << "updatable process '" << name_ << "' exited 4 times "
+ << (boot_completed ? "in 4 minutes" : "before boot completed");
// Notifies update_verifier and apexd
property_set("ro.init.updatable_crashing", "1");
}
@@ -947,6 +951,8 @@
pre_apexd_ = true;
}
+ post_data_ = ServiceList::GetInstance().IsPostData();
+
LOG(INFO) << "starting service '" << name_ << "'...";
pid_t pid = -1;
@@ -1146,6 +1152,12 @@
StopOrReset(SVC_RESET);
}
+void Service::ResetIfPostData() {
+ if (post_data_) {
+ StopOrReset(SVC_RESET);
+ }
+}
+
void Service::Stop() {
StopOrReset(SVC_DISABLED);
}
@@ -1339,6 +1351,14 @@
}
}
+void ServiceList::MarkPostData() {
+ post_data_ = true;
+}
+
+bool ServiceList::IsPostData() {
+ return post_data_;
+}
+
void ServiceList::MarkServicesUpdate() {
services_update_finished_ = true;
diff --git a/init/service.h b/init/service.h
index c42a5a3..dc2b128 100644
--- a/init/service.h
+++ b/init/service.h
@@ -81,6 +81,7 @@
Result<Success> StartIfNotDisabled();
Result<Success> Enable();
void Reset();
+ void ResetIfPostData();
void Stop();
void Terminate();
void Timeout();
@@ -124,6 +125,7 @@
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_; }
+ bool is_post_data() const { return post_data_; }
private:
using OptionParser = Result<Success> (Service::*)(std::vector<std::string>&& args);
@@ -244,6 +246,8 @@
std::vector<std::function<void(const siginfo_t& siginfo)>> reap_callbacks_;
bool pre_apexd_ = false;
+
+ bool post_data_ = false;
};
class ServiceList {
@@ -285,6 +289,8 @@
const std::vector<std::unique_ptr<Service>>& services() const { return services_; }
const std::vector<Service*> services_in_shutdown_order() const;
+ void MarkPostData();
+ bool IsPostData();
void MarkServicesUpdate();
bool IsServicesUpdated() const { return services_update_finished_; }
void DelayService(const Service& service);
@@ -292,6 +298,7 @@
private:
std::vector<std::unique_ptr<Service>> services_;
+ bool post_data_ = false;
bool services_update_finished_ = false;
std::vector<std::string> delayed_service_names_;
};
diff --git a/init/sigchld_handler.cpp b/init/sigchld_handler.cpp
index 0b03324..987b2f9 100644
--- a/init/sigchld_handler.cpp
+++ b/init/sigchld_handler.cpp
@@ -29,7 +29,6 @@
#include <android-base/stringprintf.h>
#include "init.h"
-#include "property_service.h"
#include "service.h"
using android::base::StringPrintf;
@@ -61,9 +60,7 @@
std::string wait_string;
Service* service = nullptr;
- if (PropertyChildReap(pid)) {
- name = "Async property child";
- } else if (SubcontextChildReap(pid)) {
+ if (SubcontextChildReap(pid)) {
name = "Subcontext";
} else {
service = ServiceList::GetInstance().FindService(pid, &Service::pid);
diff --git a/libion/ion_4.12.h b/libion/ion_4.12.h
index 6ae79d4..614510c 100644
--- a/libion/ion_4.12.h
+++ b/libion/ion_4.12.h
@@ -1,125 +1,50 @@
-/*
- * Adapted from drivers/staging/android/uapi/ion.h
- *
- * Copyright (C) 2011 Google, Inc.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
+/****************************************************************************
+ ****************************************************************************
+ ***
+ *** This header was automatically generated from a Linux kernel header
+ *** of the same name, to make information necessary for userspace to
+ *** call into the kernel available to libc. It contains only constants,
+ *** structures, and macros generated from the original header, and thus,
+ *** contains no copyrightable information.
+ ***
+ *** To edit the content of this header, modify the corresponding
+ *** source file (e.g. under external/kernel-headers/original/) then
+ *** run bionic/libc/kernel/tools/update_all.py
+ ***
+ *** Any manual change here will be lost the next time this script will
+ *** be run. You've been warned!
+ ***
+ ****************************************************************************
+ ****************************************************************************/
#ifndef _UAPI_LINUX_ION_NEW_H
#define _UAPI_LINUX_ION_NEW_H
-
#include <linux/ioctl.h>
#include <linux/types.h>
-
#define ION_NUM_HEAP_IDS (sizeof(unsigned int) * 8)
-
-/**
- * DOC: Ion Userspace API
- *
- * create a client by opening /dev/ion
- * most operations handled via following ioctls
- *
- */
-
-/**
- * struct ion_new_allocation_data - metadata passed from userspace for allocations
- * @len: size of the allocation
- * @heap_id_mask: mask of heap ids to allocate from
- * @flags: flags passed to heap
- * @handle: pointer that will be populated with a cookie to use to
- * refer to this allocation
- *
- * Provided by userspace as an argument to the ioctl - added _new to denote
- * this belongs to the new ION interface.
- */
struct ion_new_allocation_data {
- __u64 len;
- __u32 heap_id_mask;
- __u32 flags;
- __u32 fd;
- __u32 unused;
+ __u64 len;
+ __u32 heap_id_mask;
+ __u32 flags;
+ __u32 fd;
+ __u32 unused;
};
-
#define MAX_HEAP_NAME 32
-
-/**
- * struct ion_heap_data - data about a heap
- * @name - first 32 characters of the heap name
- * @type - heap type
- * @heap_id - heap id for the heap
- */
struct ion_heap_data {
- char name[MAX_HEAP_NAME];
- __u32 type;
- __u32 heap_id;
- __u32 reserved0;
- __u32 reserved1;
- __u32 reserved2;
+ char name[MAX_HEAP_NAME];
+ __u32 type;
+ __u32 heap_id;
+ __u32 reserved0;
+ __u32 reserved1;
+ __u32 reserved2;
};
-
-/**
- * struct ion_heap_query - collection of data about all heaps
- * @cnt - total number of heaps to be copied
- * @heaps - buffer to copy heap data
- */
struct ion_heap_query {
- __u32 cnt; /* Total number of heaps to be copied */
- __u32 reserved0; /* align to 64bits */
- __u64 heaps; /* buffer to be populated */
- __u32 reserved1;
- __u32 reserved2;
+ __u32 cnt;
+ __u32 reserved0;
+ __u64 heaps;
+ __u32 reserved1;
+ __u32 reserved2;
};
-
#define ION_IOC_MAGIC 'I'
-
-/**
- * DOC: ION_IOC_NEW_ALLOC - allocate memory
- *
- * Takes an ion_allocation_data struct and returns it with the handle field
- * populated with the opaque handle for the allocation.
- * TODO: This IOCTL will clash by design; however, only one of
- * ION_IOC_ALLOC or ION_IOC_NEW_ALLOC paths will be exercised,
- * so this should not conflict.
- */
#define ION_IOC_NEW_ALLOC _IOWR(ION_IOC_MAGIC, 0, struct ion_new_allocation_data)
-
-/**
- * DOC: ION_IOC_FREE - free memory
- *
- * Takes an ion_handle_data struct and frees the handle.
- *
- * #define ION_IOC_FREE _IOWR(ION_IOC_MAGIC, 1, struct ion_handle_data)
- * This will come from the older kernels, so don't redefine here
- */
-
-/**
- * DOC: ION_IOC_SHARE - creates a file descriptor to use to share an allocation
- *
- * Takes an ion_fd_data struct with the handle field populated with a valid
- * opaque handle. Returns the struct with the fd field set to a file
- * descriptor open in the current address space. This file descriptor
- * can then be passed to another process. The corresponding opaque handle can
- * be retrieved via ION_IOC_IMPORT.
- *
- * #define ION_IOC_SHARE _IOWR(ION_IOC_MAGIC, 4, struct ion_fd_data)
- * This will come from the older kernels, so don't redefine here
- */
-
-/**
- * DOC: ION_IOC_HEAP_QUERY - information about available heaps
- *
- * Takes an ion_heap_query structure and populates information about
- * available Ion heaps.
- */
#define ION_IOC_HEAP_QUERY _IOWR(ION_IOC_MAGIC, 8, struct ion_heap_query)
-
-#endif /* _UAPI_LINUX_ION_NEW_H */
+#endif
diff --git a/libion/original-kernel-headers/linux/ion_4.12.h b/libion/original-kernel-headers/linux/ion_4.12.h
new file mode 100644
index 0000000..6ae79d4
--- /dev/null
+++ b/libion/original-kernel-headers/linux/ion_4.12.h
@@ -0,0 +1,125 @@
+/*
+ * Adapted from drivers/staging/android/uapi/ion.h
+ *
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _UAPI_LINUX_ION_NEW_H
+#define _UAPI_LINUX_ION_NEW_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+#define ION_NUM_HEAP_IDS (sizeof(unsigned int) * 8)
+
+/**
+ * DOC: Ion Userspace API
+ *
+ * create a client by opening /dev/ion
+ * most operations handled via following ioctls
+ *
+ */
+
+/**
+ * struct ion_new_allocation_data - metadata passed from userspace for allocations
+ * @len: size of the allocation
+ * @heap_id_mask: mask of heap ids to allocate from
+ * @flags: flags passed to heap
+ * @handle: pointer that will be populated with a cookie to use to
+ * refer to this allocation
+ *
+ * Provided by userspace as an argument to the ioctl - added _new to denote
+ * this belongs to the new ION interface.
+ */
+struct ion_new_allocation_data {
+ __u64 len;
+ __u32 heap_id_mask;
+ __u32 flags;
+ __u32 fd;
+ __u32 unused;
+};
+
+#define MAX_HEAP_NAME 32
+
+/**
+ * struct ion_heap_data - data about a heap
+ * @name - first 32 characters of the heap name
+ * @type - heap type
+ * @heap_id - heap id for the heap
+ */
+struct ion_heap_data {
+ char name[MAX_HEAP_NAME];
+ __u32 type;
+ __u32 heap_id;
+ __u32 reserved0;
+ __u32 reserved1;
+ __u32 reserved2;
+};
+
+/**
+ * struct ion_heap_query - collection of data about all heaps
+ * @cnt - total number of heaps to be copied
+ * @heaps - buffer to copy heap data
+ */
+struct ion_heap_query {
+ __u32 cnt; /* Total number of heaps to be copied */
+ __u32 reserved0; /* align to 64bits */
+ __u64 heaps; /* buffer to be populated */
+ __u32 reserved1;
+ __u32 reserved2;
+};
+
+#define ION_IOC_MAGIC 'I'
+
+/**
+ * DOC: ION_IOC_NEW_ALLOC - allocate memory
+ *
+ * Takes an ion_allocation_data struct and returns it with the handle field
+ * populated with the opaque handle for the allocation.
+ * TODO: This IOCTL will clash by design; however, only one of
+ * ION_IOC_ALLOC or ION_IOC_NEW_ALLOC paths will be exercised,
+ * so this should not conflict.
+ */
+#define ION_IOC_NEW_ALLOC _IOWR(ION_IOC_MAGIC, 0, struct ion_new_allocation_data)
+
+/**
+ * DOC: ION_IOC_FREE - free memory
+ *
+ * Takes an ion_handle_data struct and frees the handle.
+ *
+ * #define ION_IOC_FREE _IOWR(ION_IOC_MAGIC, 1, struct ion_handle_data)
+ * This will come from the older kernels, so don't redefine here
+ */
+
+/**
+ * DOC: ION_IOC_SHARE - creates a file descriptor to use to share an allocation
+ *
+ * Takes an ion_fd_data struct with the handle field populated with a valid
+ * opaque handle. Returns the struct with the fd field set to a file
+ * descriptor open in the current address space. This file descriptor
+ * can then be passed to another process. The corresponding opaque handle can
+ * be retrieved via ION_IOC_IMPORT.
+ *
+ * #define ION_IOC_SHARE _IOWR(ION_IOC_MAGIC, 4, struct ion_fd_data)
+ * This will come from the older kernels, so don't redefine here
+ */
+
+/**
+ * DOC: ION_IOC_HEAP_QUERY - information about available heaps
+ *
+ * Takes an ion_heap_query structure and populates information about
+ * available Ion heaps.
+ */
+#define ION_IOC_HEAP_QUERY _IOWR(ION_IOC_MAGIC, 8, struct ion_heap_query)
+
+#endif /* _UAPI_LINUX_ION_NEW_H */
diff --git a/liblog/include/android/log.h b/liblog/include/android/log.h
index b2f0ed9..935590d 100644
--- a/liblog/include/android/log.h
+++ b/liblog/include/android/log.h
@@ -122,9 +122,10 @@
*
* Most callers should use
* [assert(3)](http://man7.org/linux/man-pages/man3/assert.3.html) from
- * `<assert.h>` instead, or the `__assert` and `__assert2` functions provided by
- * bionic if more control is needed. They support automatically including the
- * source filename and line number more conveniently than this function.
+ * `<assert.h>` instead, or the `__assert` and `__assert2` functions
+ * provided by bionic if more control is needed. They support automatically
+ * including the source filename and line number more conveniently than this
+ * function.
*/
void __android_log_assert(const char* cond, const char* tag, const char* fmt,
...)
diff --git a/liblog/log_portability.h b/liblog/log_portability.h
index 468a498..b7279d1 100644
--- a/liblog/log_portability.h
+++ b/liblog/log_portability.h
@@ -19,16 +19,6 @@
#include <sys/cdefs.h>
#include <unistd.h>
-/*
- * Declare this library function as reimplementation.
- * Prevent circular dependencies, but allow _real_ library to hijack
- */
-#if defined(_WIN32)
-#define LIBLOG_WEAK static /* Accept that it is totally private */
-#else
-#define LIBLOG_WEAK extern "C" __attribute__((weak, visibility("default")))
-#endif
-
/* possible missing definitions in sys/cdefs.h */
/* DECLS */
diff --git a/liblog/logprint.cpp b/liblog/logprint.cpp
index bc056cb..6b5ea4c 100644
--- a/liblog/logprint.cpp
+++ b/liblog/logprint.cpp
@@ -33,6 +33,7 @@
#include <string.h>
#include <sys/param.h>
#include <sys/types.h>
+#include <wchar.h>
#include <cutils/list.h>
#include <log/log.h>
@@ -1134,66 +1135,13 @@
}
/*
- * One utf8 character at a time
- *
- * Returns the length of the utf8 character in the buffer,
- * or -1 if illegal or truncated
- *
- * Open coded from libutils/Unicode.cpp, borrowed from utf8_length(),
- * can not remove from here because of library circular dependencies.
- * Expect one-day utf8_character_length with the same signature could
- * _also_ be part of libutils/Unicode.cpp if its usefullness needs to
- * propagate globally.
- */
-LIBLOG_WEAK ssize_t utf8_character_length(const char* src, size_t len) {
- const char* cur = src;
- const char first_char = *cur++;
- static const uint32_t kUnicodeMaxCodepoint = 0x0010FFFF;
- int32_t mask, to_ignore_mask;
- size_t num_to_read;
- uint32_t utf32;
-
- if ((first_char & 0x80) == 0) { /* ASCII */
- return first_char ? 1 : -1;
- }
-
- /*
- * (UTF-8's character must not be like 10xxxxxx,
- * but 110xxxxx, 1110xxxx, ... or 1111110x)
- */
- if ((first_char & 0x40) == 0) {
- return -1;
- }
-
- for (utf32 = 1, num_to_read = 1, mask = 0x40, to_ignore_mask = 0x80;
- num_to_read < 5 && (first_char & mask); num_to_read++, to_ignore_mask |= mask, mask >>= 1) {
- if (num_to_read > len) {
- return -1;
- }
- if ((*cur & 0xC0) != 0x80) { /* can not be 10xxxxxx? */
- return -1;
- }
- utf32 = (utf32 << 6) + (*cur++ & 0b00111111);
- }
- /* "first_char" must be (110xxxxx - 11110xxx) */
- if (num_to_read >= 5) {
- return -1;
- }
- to_ignore_mask |= mask;
- utf32 |= ((~to_ignore_mask) & first_char) << (6 * (num_to_read - 1));
- if (utf32 > kUnicodeMaxCodepoint) {
- return -1;
- }
- return num_to_read;
-}
-
-/*
* Convert to printable from message to p buffer, return string length. If p is
* NULL, do not copy, but still return the expected string length.
*/
-static size_t convertPrintable(char* p, const char* message, size_t messageLen) {
+size_t convertPrintable(char* p, const char* message, size_t messageLen) {
char* begin = p;
bool print = p != NULL;
+ mbstate_t mb_state = {};
while (messageLen) {
char buf[6];
@@ -1201,11 +1149,10 @@
if ((size_t)len > messageLen) {
len = messageLen;
}
- len = utf8_character_length(message, len);
+ len = mbrtowc(nullptr, message, len, &mb_state);
if (len < 0) {
- snprintf(buf, sizeof(buf), ((messageLen > 1) && isdigit(message[1])) ? "\\%03o" : "\\%o",
- *message & 0377);
+ snprintf(buf, sizeof(buf), "\\x%02X", static_cast<unsigned char>(*message));
len = 1;
} else {
buf[0] = '\0';
@@ -1225,7 +1172,7 @@
} else if (*message == '\\') {
strcpy(buf, "\\\\");
} else if ((*message < ' ') || (*message & 0x80)) {
- snprintf(buf, sizeof(buf), "\\%o", *message & 0377);
+ snprintf(buf, sizeof(buf), "\\x%02X", static_cast<unsigned char>(*message));
}
}
if (!buf[0]) {
diff --git a/liblog/tests/Android.bp b/liblog/tests/Android.bp
index 50755ce..d9d1a21 100644
--- a/liblog/tests/Android.bp
+++ b/liblog/tests/Android.bp
@@ -62,6 +62,7 @@
"log_system_test.cpp",
"log_time_test.cpp",
"log_wrap_test.cpp",
+ "logprint_test.cpp",
],
shared_libs: [
"libcutils",
diff --git a/liblog/tests/logprint_test.cpp b/liblog/tests/logprint_test.cpp
new file mode 100644
index 0000000..7ca02ac
--- /dev/null
+++ b/liblog/tests/logprint_test.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2019 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 <gtest/gtest.h>
+
+size_t convertPrintable(char* p, const char* message, size_t messageLen);
+
+TEST(liblog, convertPrintable_ascii) {
+ auto input = "easy string, output same";
+ auto output_size = convertPrintable(nullptr, input, strlen(input));
+ EXPECT_EQ(output_size, strlen(input));
+
+ char output[output_size];
+
+ output_size = convertPrintable(output, input, strlen(input));
+ EXPECT_EQ(output_size, strlen(input));
+ EXPECT_STREQ(input, output);
+}
+
+TEST(liblog, convertPrintable_escapes) {
+ // Note that \t is not escaped.
+ auto input = "escape\a\b\t\v\f\r\\";
+ auto expected_output = "escape\\a\\b\t\\v\\f\\r\\\\";
+ auto output_size = convertPrintable(nullptr, input, strlen(input));
+ EXPECT_EQ(output_size, strlen(expected_output));
+
+ char output[output_size];
+
+ output_size = convertPrintable(output, input, strlen(input));
+ EXPECT_EQ(output_size, strlen(expected_output));
+ EXPECT_STREQ(expected_output, output);
+}
+
+TEST(liblog, convertPrintable_validutf8) {
+ auto input = u8"¢ह€𐍈";
+ auto output_size = convertPrintable(nullptr, input, strlen(input));
+ EXPECT_EQ(output_size, strlen(input));
+
+ char output[output_size];
+
+ output_size = convertPrintable(output, input, strlen(input));
+ EXPECT_EQ(output_size, strlen(input));
+ EXPECT_STREQ(input, output);
+}
+
+TEST(liblog, convertPrintable_invalidutf8) {
+ auto input = "\x80\xC2\x01\xE0\xA4\x06\xE0\x06\xF0\x90\x8D\x06\xF0\x90\x06\xF0\x0E";
+ auto expected_output =
+ "\\x80\\xC2\\x01\\xE0\\xA4\\x06\\xE0\\x06\\xF0\\x90\\x8D\\x06\\xF0\\x90\\x06\\xF0\\x0E";
+ auto output_size = convertPrintable(nullptr, input, strlen(input));
+ EXPECT_EQ(output_size, strlen(expected_output));
+
+ char output[output_size];
+
+ output_size = convertPrintable(output, input, strlen(input));
+ EXPECT_EQ(output_size, strlen(expected_output));
+ EXPECT_STREQ(expected_output, output);
+}
+
+TEST(liblog, convertPrintable_mixed) {
+ auto input =
+ u8"\x80\xC2¢ह€𐍈\x01\xE0\xA4\x06¢ह€𐍈\xE0\x06\a\b\xF0\x90¢ह€𐍈\x8D\x06\xF0\t\t\x90\x06\xF0\x0E";
+ auto expected_output =
+ u8"\\x80\\xC2¢ह€𐍈\\x01\\xE0\\xA4\\x06¢ह€𐍈\\xE0\\x06\\a\\b\\xF0\\x90¢ह€𐍈\\x8D\\x06\\xF0\t\t"
+ u8"\\x90\\x06\\xF0\\x0E";
+ auto output_size = convertPrintable(nullptr, input, strlen(input));
+ EXPECT_EQ(output_size, strlen(expected_output));
+
+ char output[output_size];
+
+ output_size = convertPrintable(output, input, strlen(input));
+ EXPECT_EQ(output_size, strlen(expected_output));
+ EXPECT_STREQ(expected_output, output);
+}
diff --git a/libnativeloader/Android.bp b/libnativeloader/Android.bp
index 66cb49f..4d65b50 100644
--- a/libnativeloader/Android.bp
+++ b/libnativeloader/Android.bp
@@ -17,7 +17,9 @@
name: "libnativeloader",
defaults: ["libnativeloader-defaults"],
host_supported: true,
- srcs: ["native_loader.cpp"],
+ srcs: [
+ "native_loader.cpp",
+ ],
shared_libs: [
"libnativehelper",
"liblog",
@@ -26,6 +28,9 @@
],
target: {
android: {
+ srcs: [
+ "library_namespaces.cpp",
+ ],
shared_libs: [
"libdl_android",
],
diff --git a/libnativeloader/library_namespaces.cpp b/libnativeloader/library_namespaces.cpp
new file mode 100644
index 0000000..3c4911f
--- /dev/null
+++ b/libnativeloader/library_namespaces.cpp
@@ -0,0 +1,660 @@
+/*
+ * Copyright (C) 2019 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 "library_namespaces.h"
+
+#include <dirent.h>
+#include <dlfcn.h>
+
+#include <regex>
+#include <string>
+#include <vector>
+
+#include "android-base/file.h"
+#include "android-base/logging.h"
+#include "android-base/macros.h"
+#include "android-base/properties.h"
+#include "android-base/strings.h"
+#include "nativehelper/ScopedUtfChars.h"
+#include "nativeloader/dlext_namespaces.h"
+
+namespace android {
+
+namespace {
+using namespace std::string_literals;
+
+constexpr const char kPublicNativeLibrariesSystemConfigPathFromRoot[] = "/etc/public.libraries.txt";
+constexpr const char kPublicNativeLibrariesExtensionConfigPrefix[] = "public.libraries-";
+constexpr const size_t kPublicNativeLibrariesExtensionConfigPrefixLen =
+ sizeof(kPublicNativeLibrariesExtensionConfigPrefix) - 1;
+constexpr const char kPublicNativeLibrariesExtensionConfigSuffix[] = ".txt";
+constexpr const size_t kPublicNativeLibrariesExtensionConfigSuffixLen =
+ sizeof(kPublicNativeLibrariesExtensionConfigSuffix) - 1;
+constexpr const char kPublicNativeLibrariesVendorConfig[] = "/vendor/etc/public.libraries.txt";
+constexpr const char kLlndkNativeLibrariesSystemConfigPathFromRoot[] = "/etc/llndk.libraries.txt";
+constexpr const char kVndkspNativeLibrariesSystemConfigPathFromRoot[] = "/etc/vndksp.libraries.txt";
+
+const std::vector<const std::string> kRuntimePublicLibraries = {
+ "libicuuc.so",
+ "libicui18n.so",
+};
+
+// The device may be configured to have the vendor libraries loaded to a separate namespace.
+// For historical reasons this namespace was named sphal but effectively it is intended
+// to use to load vendor libraries to separate namespace with controlled interface between
+// vendor and system namespaces.
+constexpr const char* kVendorNamespaceName = "sphal";
+constexpr const char* kVndkNamespaceName = "vndk";
+constexpr const char* kDefaultNamespaceName = "default";
+constexpr const char* kPlatformNamespaceName = "platform";
+constexpr const char* kRuntimeNamespaceName = "runtime";
+
+// classloader-namespace is a linker namespace that is created for the loaded
+// app. To be specific, it is created for the app classloader. When
+// System.load() is called from a Java class that is loaded from the
+// classloader, the classloader-namespace namespace associated with that
+// classloader is selected for dlopen. The namespace is configured so that its
+// search path is set to the app-local JNI directory and it is linked to the
+// platform namespace with the names of libs listed in the public.libraries.txt.
+// This way an app can only load its own JNI libraries along with the public libs.
+constexpr const char* kClassloaderNamespaceName = "classloader-namespace";
+// Same thing for vendor APKs.
+constexpr const char* kVendorClassloaderNamespaceName = "vendor-classloader-namespace";
+
+// (http://b/27588281) This is a workaround for apps using custom classloaders and calling
+// System.load() with an absolute path which is outside of the classloader library search path.
+// This list includes all directories app is allowed to access this way.
+constexpr const char* kWhitelistedDirectories = "/data:/mnt/expand";
+
+#if defined(__LP64__)
+constexpr const char* kRuntimeApexLibPath = "/apex/com.android.runtime/lib64";
+constexpr const char* kVendorLibPath = "/vendor/lib64";
+constexpr const char* kProductLibPath = "/product/lib64:/system/product/lib64";
+#else
+constexpr const char* kRuntimeApexLibPath = "/apex/com.android.runtime/lib";
+constexpr const char* kVendorLibPath = "/vendor/lib";
+constexpr const char* kProductLibPath = "/product/lib:/system/product/lib";
+#endif
+
+const std::regex kVendorDexPathRegex("(^|:)/vendor/");
+const std::regex kProductDexPathRegex("(^|:)(/system)?/product/");
+
+// Define origin of APK if it is from vendor partition or product partition
+typedef enum {
+ APK_ORIGIN_DEFAULT = 0,
+ APK_ORIGIN_VENDOR = 1,
+ APK_ORIGIN_PRODUCT = 2,
+} ApkOrigin;
+
+bool is_debuggable() {
+ bool debuggable = false;
+ debuggable = android::base::GetBoolProperty("ro.debuggable", false);
+ return debuggable;
+}
+
+std::string vndk_version_str() {
+ std::string version = android::base::GetProperty("ro.vndk.version", "");
+ if (version != "" && version != "current") {
+ return "." + version;
+ }
+ return "";
+}
+
+void insert_vndk_version_str(std::string* file_name) {
+ CHECK(file_name != nullptr);
+ size_t insert_pos = file_name->find_last_of(".");
+ if (insert_pos == std::string::npos) {
+ insert_pos = file_name->length();
+ }
+ file_name->insert(insert_pos, vndk_version_str());
+}
+
+const std::function<bool(const std::string&, std::string*)> always_true =
+ [](const std::string&, std::string*) { return true; };
+
+bool ReadConfig(const std::string& configFile, std::vector<std::string>* sonames,
+ const std::function<bool(const std::string& /* soname */,
+ std::string* /* error_msg */)>& check_soname,
+ std::string* error_msg = nullptr) {
+ // Read list of public native libraries from the config file.
+ std::string file_content;
+ if (!base::ReadFileToString(configFile, &file_content)) {
+ if (error_msg) *error_msg = strerror(errno);
+ return false;
+ }
+
+ std::vector<std::string> lines = base::Split(file_content, "\n");
+
+ for (auto& line : lines) {
+ auto trimmed_line = base::Trim(line);
+ if (trimmed_line[0] == '#' || trimmed_line.empty()) {
+ continue;
+ }
+ size_t space_pos = trimmed_line.rfind(' ');
+ if (space_pos != std::string::npos) {
+ std::string type = trimmed_line.substr(space_pos + 1);
+ if (type != "32" && type != "64") {
+ if (error_msg) *error_msg = "Malformed line: " + line;
+ return false;
+ }
+#if defined(__LP64__)
+ // Skip 32 bit public library.
+ if (type == "32") {
+ continue;
+ }
+#else
+ // Skip 64 bit public library.
+ if (type == "64") {
+ continue;
+ }
+#endif
+ trimmed_line.resize(space_pos);
+ }
+
+ if (check_soname(trimmed_line, error_msg)) {
+ sonames->push_back(trimmed_line);
+ } else {
+ return false;
+ }
+ }
+ return true;
+}
+
+void ReadExtensionLibraries(const char* dirname, std::vector<std::string>* sonames) {
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(dirname), closedir);
+ if (dir != nullptr) {
+ // Failing to opening the dir is not an error, which can happen in
+ // webview_zygote.
+ while (struct dirent* ent = readdir(dir.get())) {
+ if (ent->d_type != DT_REG && ent->d_type != DT_LNK) {
+ continue;
+ }
+ const std::string filename(ent->d_name);
+ if (android::base::StartsWith(filename, kPublicNativeLibrariesExtensionConfigPrefix) &&
+ android::base::EndsWith(filename, kPublicNativeLibrariesExtensionConfigSuffix)) {
+ const size_t start = kPublicNativeLibrariesExtensionConfigPrefixLen;
+ const size_t end = filename.size() - kPublicNativeLibrariesExtensionConfigSuffixLen;
+ const std::string company_name = filename.substr(start, end - start);
+ const std::string config_file_path = dirname + "/"s + filename;
+ LOG_ALWAYS_FATAL_IF(
+ company_name.empty(),
+ "Error extracting company name from public native library list file path \"%s\"",
+ config_file_path.c_str());
+
+ std::string error_msg;
+
+ LOG_ALWAYS_FATAL_IF(
+ !ReadConfig(config_file_path, sonames,
+ [&company_name](const std::string& soname, std::string* error_msg) {
+ if (android::base::StartsWith(soname, "lib") &&
+ android::base::EndsWith(soname, "." + company_name + ".so")) {
+ return true;
+ } else {
+ *error_msg = "Library name \"" + soname +
+ "\" does not end with the company name: " + company_name +
+ ".";
+ return false;
+ }
+ },
+ &error_msg),
+ "Error reading public native library list from \"%s\": %s", config_file_path.c_str(),
+ error_msg.c_str());
+ }
+ }
+ }
+}
+
+/**
+ * Remove the public libs in runtime namespace
+ */
+void removePublicLibsIfExistsInRuntimeApex(std::vector<std::string>& sonames) {
+ for (const std::string& lib_name : kRuntimePublicLibraries) {
+ std::string path(kRuntimeApexLibPath);
+ path.append("/").append(lib_name);
+
+ struct stat s;
+ // Do nothing if the path in /apex does not exist.
+ // Runtime APEX must be mounted since libnativeloader is in the same APEX
+ if (stat(path.c_str(), &s) != 0) {
+ continue;
+ }
+
+ auto it = std::find(sonames.begin(), sonames.end(), lib_name);
+ if (it != sonames.end()) {
+ sonames.erase(it);
+ }
+ }
+}
+
+jobject GetParentClassLoader(JNIEnv* env, jobject class_loader) {
+ jclass class_loader_class = env->FindClass("java/lang/ClassLoader");
+ jmethodID get_parent =
+ env->GetMethodID(class_loader_class, "getParent", "()Ljava/lang/ClassLoader;");
+
+ return env->CallObjectMethod(class_loader, get_parent);
+}
+
+ApkOrigin GetApkOriginFromDexPath(JNIEnv* env, jstring dex_path) {
+ ApkOrigin apk_origin = APK_ORIGIN_DEFAULT;
+
+ if (dex_path != nullptr) {
+ ScopedUtfChars dex_path_utf_chars(env, dex_path);
+
+ if (std::regex_search(dex_path_utf_chars.c_str(), kVendorDexPathRegex)) {
+ apk_origin = APK_ORIGIN_VENDOR;
+ }
+
+ if (std::regex_search(dex_path_utf_chars.c_str(), kProductDexPathRegex)) {
+ LOG_ALWAYS_FATAL_IF(apk_origin == APK_ORIGIN_VENDOR,
+ "Dex path contains both vendor and product partition : %s",
+ dex_path_utf_chars.c_str());
+
+ apk_origin = APK_ORIGIN_PRODUCT;
+ }
+ }
+ return apk_origin;
+}
+
+} // namespace
+
+void LibraryNamespaces::Initialize() {
+ // Once public namespace is initialized there is no
+ // point in running this code - it will have no effect
+ // on the current list of public libraries.
+ if (initialized_) {
+ return;
+ }
+
+ std::vector<std::string> sonames;
+ const char* android_root_env = getenv("ANDROID_ROOT");
+ std::string root_dir = android_root_env != nullptr ? android_root_env : "/system";
+ std::string public_native_libraries_system_config =
+ root_dir + kPublicNativeLibrariesSystemConfigPathFromRoot;
+ std::string runtime_public_libraries = base::Join(kRuntimePublicLibraries, ":");
+ std::string llndk_native_libraries_system_config =
+ root_dir + kLlndkNativeLibrariesSystemConfigPathFromRoot;
+ std::string vndksp_native_libraries_system_config =
+ root_dir + kVndkspNativeLibrariesSystemConfigPathFromRoot;
+
+ std::string product_public_native_libraries_dir = "/product/etc";
+
+ std::string error_msg;
+ LOG_ALWAYS_FATAL_IF(
+ !ReadConfig(public_native_libraries_system_config, &sonames, always_true, &error_msg),
+ "Error reading public native library list from \"%s\": %s",
+ public_native_libraries_system_config.c_str(), error_msg.c_str());
+
+ // For debuggable platform builds use ANDROID_ADDITIONAL_PUBLIC_LIBRARIES environment
+ // variable to add libraries to the list. This is intended for platform tests only.
+ if (is_debuggable()) {
+ const char* additional_libs = getenv("ANDROID_ADDITIONAL_PUBLIC_LIBRARIES");
+ if (additional_libs != nullptr && additional_libs[0] != '\0') {
+ std::vector<std::string> additional_libs_vector = base::Split(additional_libs, ":");
+ std::copy(additional_libs_vector.begin(), additional_libs_vector.end(),
+ std::back_inserter(sonames));
+ // Apply the same list to the runtime namespace, since some libraries
+ // might reside there.
+ CHECK(sizeof(kRuntimePublicLibraries) > 0);
+ runtime_public_libraries = runtime_public_libraries + ':' + additional_libs;
+ }
+ }
+
+ // Remove the public libs in the runtime namespace.
+ // These libs are listed in public.android.txt, but we don't want the rest of android
+ // in default namespace to dlopen the libs.
+ // For example, libicuuc.so is exposed to classloader namespace from runtime namespace.
+ // Unfortunately, it does not have stable C symbols, and default namespace should only use
+ // stable symbols in libandroidicu.so. http://b/120786417
+ removePublicLibsIfExistsInRuntimeApex(sonames);
+
+ // android_init_namespaces() expects all the public libraries
+ // to be loaded so that they can be found by soname alone.
+ //
+ // TODO(dimitry): this is a bit misleading since we do not know
+ // if the vendor public library is going to be opened from /vendor/lib
+ // we might as well end up loading them from /system/lib or /product/lib
+ // For now we rely on CTS test to catch things like this but
+ // it should probably be addressed in the future.
+ for (const auto& soname : sonames) {
+ LOG_ALWAYS_FATAL_IF(dlopen(soname.c_str(), RTLD_NOW | RTLD_NODELETE) == nullptr,
+ "Error preloading public library %s: %s", soname.c_str(), dlerror());
+ }
+
+ system_public_libraries_ = base::Join(sonames, ':');
+ runtime_public_libraries_ = runtime_public_libraries;
+
+ // read /system/etc/public.libraries-<companyname>.txt which contain partner defined
+ // system libs that are exposed to apps. The libs in the txt files must be
+ // named as lib<name>.<companyname>.so.
+ sonames.clear();
+ ReadExtensionLibraries(base::Dirname(public_native_libraries_system_config).c_str(), &sonames);
+ oem_public_libraries_ = base::Join(sonames, ':');
+
+ // read /product/etc/public.libraries-<companyname>.txt which contain partner defined
+ // product libs that are exposed to apps.
+ sonames.clear();
+ ReadExtensionLibraries(product_public_native_libraries_dir.c_str(), &sonames);
+ product_public_libraries_ = base::Join(sonames, ':');
+
+ // Insert VNDK version to llndk and vndksp config file names.
+ insert_vndk_version_str(&llndk_native_libraries_system_config);
+ insert_vndk_version_str(&vndksp_native_libraries_system_config);
+
+ sonames.clear();
+ ReadConfig(llndk_native_libraries_system_config, &sonames, always_true);
+ system_llndk_libraries_ = base::Join(sonames, ':');
+
+ sonames.clear();
+ ReadConfig(vndksp_native_libraries_system_config, &sonames, always_true);
+ system_vndksp_libraries_ = base::Join(sonames, ':');
+
+ sonames.clear();
+ // This file is optional, quietly ignore if the file does not exist.
+ ReadConfig(kPublicNativeLibrariesVendorConfig, &sonames, always_true, nullptr);
+
+ vendor_public_libraries_ = base::Join(sonames, ':');
+}
+
+NativeLoaderNamespace* LibraryNamespaces::Create(JNIEnv* env, uint32_t target_sdk_version,
+ jobject class_loader, bool is_shared,
+ jstring dex_path, jstring java_library_path,
+ jstring java_permitted_path,
+ std::string* error_msg) {
+ std::string library_path; // empty string by default.
+
+ if (java_library_path != nullptr) {
+ ScopedUtfChars library_path_utf_chars(env, java_library_path);
+ library_path = library_path_utf_chars.c_str();
+ }
+
+ ApkOrigin apk_origin = GetApkOriginFromDexPath(env, dex_path);
+
+ // (http://b/27588281) This is a workaround for apps using custom
+ // classloaders and calling System.load() with an absolute path which
+ // is outside of the classloader library search path.
+ //
+ // This part effectively allows such a classloader to access anything
+ // under /data and /mnt/expand
+ std::string permitted_path = kWhitelistedDirectories;
+
+ if (java_permitted_path != nullptr) {
+ ScopedUtfChars path(env, java_permitted_path);
+ if (path.c_str() != nullptr && path.size() > 0) {
+ permitted_path = permitted_path + ":" + path.c_str();
+ }
+ }
+
+ // Initialize the anonymous namespace with the first non-empty library path.
+ if (!library_path.empty() && !initialized_ &&
+ !InitPublicNamespace(library_path.c_str(), error_msg)) {
+ return nullptr;
+ }
+
+ bool found = FindNamespaceByClassLoader(env, class_loader);
+
+ LOG_ALWAYS_FATAL_IF(found, "There is already a namespace associated with this classloader");
+
+ uint64_t namespace_type = ANDROID_NAMESPACE_TYPE_ISOLATED;
+ if (is_shared) {
+ namespace_type |= ANDROID_NAMESPACE_TYPE_SHARED;
+ }
+
+ if (target_sdk_version < 24) {
+ namespace_type |= ANDROID_NAMESPACE_TYPE_GREYLIST_ENABLED;
+ }
+
+ NativeLoaderNamespace* parent_ns = FindParentNamespaceByClassLoader(env, class_loader);
+
+ bool is_native_bridge = false;
+
+ if (parent_ns != nullptr) {
+ is_native_bridge = !parent_ns->is_android_namespace();
+ } else if (!library_path.empty()) {
+ is_native_bridge = NativeBridgeIsPathSupported(library_path.c_str());
+ }
+
+ std::string system_exposed_libraries = system_public_libraries_;
+ const char* namespace_name = kClassloaderNamespaceName;
+ android_namespace_t* vndk_ns = nullptr;
+ if ((apk_origin == APK_ORIGIN_VENDOR ||
+ (apk_origin == APK_ORIGIN_PRODUCT && target_sdk_version > 29)) &&
+ !is_shared) {
+ LOG_FATAL_IF(is_native_bridge,
+ "Unbundled vendor / product apk must not use translated architecture");
+
+ // For vendor / product apks, give access to the vendor / product lib even though
+ // they are treated as unbundled; the libs and apks are still bundled
+ // together in the vendor / product partition.
+ const char* origin_partition;
+ const char* origin_lib_path;
+
+ switch (apk_origin) {
+ case APK_ORIGIN_VENDOR:
+ origin_partition = "vendor";
+ origin_lib_path = kVendorLibPath;
+ break;
+ case APK_ORIGIN_PRODUCT:
+ origin_partition = "product";
+ origin_lib_path = kProductLibPath;
+ break;
+ default:
+ origin_partition = "unknown";
+ origin_lib_path = "";
+ }
+
+ LOG_FATAL_IF(is_native_bridge, "Unbundled %s apk must not use translated architecture",
+ origin_partition);
+
+ library_path = library_path + ":" + origin_lib_path;
+ permitted_path = permitted_path + ":" + origin_lib_path;
+
+ // Also give access to LLNDK libraries since they are available to vendors
+ system_exposed_libraries = system_exposed_libraries + ":" + system_llndk_libraries_.c_str();
+
+ // Give access to VNDK-SP libraries from the 'vndk' namespace.
+ vndk_ns = android_get_exported_namespace(kVndkNamespaceName);
+ if (vndk_ns == nullptr) {
+ ALOGW("Cannot find \"%s\" namespace for %s apks", kVndkNamespaceName, origin_partition);
+ }
+
+ // Different name is useful for debugging
+ namespace_name = kVendorClassloaderNamespaceName;
+ ALOGD("classloader namespace configured for unbundled %s apk. library_path=%s",
+ origin_partition, library_path.c_str());
+ } else {
+ // oem and product public libraries are NOT available to vendor apks, otherwise it
+ // would be system->vendor violation.
+ if (!oem_public_libraries_.empty()) {
+ system_exposed_libraries = system_exposed_libraries + ':' + oem_public_libraries_;
+ }
+ if (!product_public_libraries_.empty()) {
+ system_exposed_libraries = system_exposed_libraries + ':' + product_public_libraries_;
+ }
+ }
+ std::string runtime_exposed_libraries = runtime_public_libraries_;
+
+ NativeLoaderNamespace native_loader_ns;
+ if (!is_native_bridge) {
+ // The platform namespace is called "default" for binaries in /system and
+ // "platform" for those in the Runtime APEX. Try "platform" first since
+ // "default" always exists.
+ android_namespace_t* platform_ns = android_get_exported_namespace(kPlatformNamespaceName);
+ if (platform_ns == nullptr) {
+ platform_ns = android_get_exported_namespace(kDefaultNamespaceName);
+ }
+
+ android_namespace_t* android_parent_ns;
+ if (parent_ns != nullptr) {
+ android_parent_ns = parent_ns->get_android_ns();
+ } else {
+ // Fall back to the platform namespace if no parent is found.
+ android_parent_ns = platform_ns;
+ }
+
+ android_namespace_t* ns =
+ android_create_namespace(namespace_name, nullptr, library_path.c_str(), namespace_type,
+ permitted_path.c_str(), android_parent_ns);
+ if (ns == nullptr) {
+ *error_msg = dlerror();
+ return nullptr;
+ }
+
+ // Note that when vendor_ns is not configured this function will return nullptr
+ // and it will result in linking vendor_public_libraries_ to the default namespace
+ // which is expected behavior in this case.
+ android_namespace_t* vendor_ns = android_get_exported_namespace(kVendorNamespaceName);
+
+ android_namespace_t* runtime_ns = android_get_exported_namespace(kRuntimeNamespaceName);
+
+ if (!android_link_namespaces(ns, platform_ns, system_exposed_libraries.c_str())) {
+ *error_msg = dlerror();
+ return nullptr;
+ }
+
+ // Runtime apex does not exist in host, and under certain build conditions.
+ if (runtime_ns != nullptr) {
+ if (!android_link_namespaces(ns, runtime_ns, runtime_exposed_libraries.c_str())) {
+ *error_msg = dlerror();
+ return nullptr;
+ }
+ }
+
+ if (vndk_ns != nullptr && !system_vndksp_libraries_.empty()) {
+ // vendor apks are allowed to use VNDK-SP libraries.
+ if (!android_link_namespaces(ns, vndk_ns, system_vndksp_libraries_.c_str())) {
+ *error_msg = dlerror();
+ return nullptr;
+ }
+ }
+
+ if (!vendor_public_libraries_.empty()) {
+ if (!android_link_namespaces(ns, vendor_ns, vendor_public_libraries_.c_str())) {
+ *error_msg = dlerror();
+ return nullptr;
+ }
+ }
+
+ native_loader_ns = NativeLoaderNamespace(ns);
+ } else {
+ // Same functionality as in the branch above, but calling through native bridge.
+
+ native_bridge_namespace_t* platform_ns =
+ NativeBridgeGetExportedNamespace(kPlatformNamespaceName);
+ if (platform_ns == nullptr) {
+ platform_ns = NativeBridgeGetExportedNamespace(kDefaultNamespaceName);
+ }
+
+ native_bridge_namespace_t* native_bridge_parent_namespace;
+ if (parent_ns != nullptr) {
+ native_bridge_parent_namespace = parent_ns->get_native_bridge_ns();
+ } else {
+ native_bridge_parent_namespace = platform_ns;
+ }
+
+ native_bridge_namespace_t* ns =
+ NativeBridgeCreateNamespace(namespace_name, nullptr, library_path.c_str(), namespace_type,
+ permitted_path.c_str(), native_bridge_parent_namespace);
+ if (ns == nullptr) {
+ *error_msg = NativeBridgeGetError();
+ return nullptr;
+ }
+
+ native_bridge_namespace_t* vendor_ns = NativeBridgeGetExportedNamespace(kVendorNamespaceName);
+ native_bridge_namespace_t* runtime_ns = NativeBridgeGetExportedNamespace(kRuntimeNamespaceName);
+
+ if (!NativeBridgeLinkNamespaces(ns, platform_ns, system_exposed_libraries.c_str())) {
+ *error_msg = NativeBridgeGetError();
+ return nullptr;
+ }
+
+ // Runtime apex does not exist in host, and under certain build conditions.
+ if (runtime_ns != nullptr) {
+ if (!NativeBridgeLinkNamespaces(ns, runtime_ns, runtime_exposed_libraries.c_str())) {
+ *error_msg = NativeBridgeGetError();
+ return nullptr;
+ }
+ }
+ if (!vendor_public_libraries_.empty()) {
+ if (!NativeBridgeLinkNamespaces(ns, vendor_ns, vendor_public_libraries_.c_str())) {
+ *error_msg = NativeBridgeGetError();
+ return nullptr;
+ }
+ }
+
+ native_loader_ns = NativeLoaderNamespace(ns);
+ }
+
+ namespaces_.push_back(std::make_pair(env->NewWeakGlobalRef(class_loader), native_loader_ns));
+
+ return &(namespaces_.back().second);
+}
+
+NativeLoaderNamespace* LibraryNamespaces::FindNamespaceByClassLoader(JNIEnv* env,
+ jobject class_loader) {
+ auto it = std::find_if(namespaces_.begin(), namespaces_.end(),
+ [&](const std::pair<jweak, NativeLoaderNamespace>& value) {
+ return env->IsSameObject(value.first, class_loader);
+ });
+ if (it != namespaces_.end()) {
+ return &it->second;
+ }
+
+ return nullptr;
+}
+
+bool LibraryNamespaces::InitPublicNamespace(const char* library_path, std::string* error_msg) {
+ // Ask native bride if this apps library path should be handled by it
+ bool is_native_bridge = NativeBridgeIsPathSupported(library_path);
+
+ // (http://b/25844435) - Some apps call dlopen from generated code (mono jited
+ // code is one example) unknown to linker in which case linker uses anonymous
+ // namespace. The second argument specifies the search path for the anonymous
+ // namespace which is the library_path of the classloader.
+ initialized_ = android_init_anonymous_namespace(system_public_libraries_.c_str(),
+ is_native_bridge ? nullptr : library_path);
+ if (!initialized_) {
+ *error_msg = dlerror();
+ return false;
+ }
+
+ // and now initialize native bridge namespaces if necessary.
+ if (NativeBridgeInitialized()) {
+ initialized_ = NativeBridgeInitAnonymousNamespace(system_public_libraries_.c_str(),
+ is_native_bridge ? library_path : nullptr);
+ if (!initialized_) {
+ *error_msg = NativeBridgeGetError();
+ }
+ }
+
+ return initialized_;
+}
+
+NativeLoaderNamespace* LibraryNamespaces::FindParentNamespaceByClassLoader(JNIEnv* env,
+ jobject class_loader) {
+ jobject parent_class_loader = GetParentClassLoader(env, class_loader);
+
+ while (parent_class_loader != nullptr) {
+ NativeLoaderNamespace* ns;
+ if ((ns = FindNamespaceByClassLoader(env, parent_class_loader)) != nullptr) {
+ return ns;
+ }
+
+ parent_class_loader = GetParentClassLoader(env, parent_class_loader);
+ }
+
+ return nullptr;
+}
+
+} // namespace android
diff --git a/libnativeloader/library_namespaces.h b/libnativeloader/library_namespaces.h
new file mode 100644
index 0000000..353b5fc
--- /dev/null
+++ b/libnativeloader/library_namespaces.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2019 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
+#if !defined(__ANDROID__)
+#error "Not available for host"
+#endif
+
+#include "native_loader_namespace.h"
+
+#include <list>
+#include <string>
+
+#include "jni.h"
+
+namespace android {
+
+// LibraryNamespaces is a singleton object that manages NativeLoaderNamespace
+// objects for an app process. Its main job is to create (and configure) a new
+// NativeLoaderNamespace object for a Java ClassLoader, and to find an existing
+// object for a given ClassLoader.
+class LibraryNamespaces {
+ public:
+ LibraryNamespaces() : initialized_(false) {}
+
+ LibraryNamespaces(LibraryNamespaces&&) = default;
+ LibraryNamespaces(const LibraryNamespaces&) = delete;
+ LibraryNamespaces& operator=(const LibraryNamespaces&) = delete;
+
+ void Initialize();
+ void Reset() { namespaces_.clear(); }
+ NativeLoaderNamespace* Create(JNIEnv* env, uint32_t target_sdk_version, jobject class_loader,
+ bool is_shared, jstring dex_path, jstring java_library_path,
+ jstring java_permitted_path, std::string* error_msg);
+ NativeLoaderNamespace* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader);
+
+ private:
+ bool InitPublicNamespace(const char* library_path, std::string* error_msg);
+ NativeLoaderNamespace* FindParentNamespaceByClassLoader(JNIEnv* env, jobject class_loader);
+
+ bool initialized_;
+ std::list<std::pair<jweak, NativeLoaderNamespace>> namespaces_;
+ std::string system_public_libraries_;
+ std::string runtime_public_libraries_;
+ std::string vendor_public_libraries_;
+ std::string oem_public_libraries_;
+ std::string product_public_libraries_;
+ std::string system_llndk_libraries_;
+ std::string system_vndksp_libraries_;
+};
+
+} // namespace android
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 5cc0857..ed98714 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -15,725 +15,59 @@
*/
#include "nativeloader/native_loader.h"
-#include <nativehelper/ScopedUtfChars.h>
+#define LOG_TAG "nativeloader"
#include <dlfcn.h>
-#ifdef __ANDROID__
-#define LOG_TAG "libnativeloader"
-#include "nativeloader/dlext_namespaces.h"
-#include "log/log.h"
-#endif
-#include <dirent.h>
#include <sys/types.h>
-#include "nativebridge/native_bridge.h"
-#include <algorithm>
-#include <list>
#include <memory>
#include <mutex>
-#include <regex>
#include <string>
#include <vector>
-#include <android-base/file.h>
-#include <android-base/macros.h>
-#include <android-base/strings.h>
-
-#ifdef __BIONIC__
-#include <android-base/properties.h>
+#include "android-base/file.h"
+#include "android-base/macros.h"
+#include "android-base/strings.h"
+#ifdef __ANDROID__
+#include "library_namespaces.h"
+#include "log/log.h"
+#include "nativeloader/dlext_namespaces.h"
#endif
-
-#define CHECK(predicate) LOG_ALWAYS_FATAL_IF(!(predicate),\
- "%s:%d: %s CHECK '" #predicate "' failed.",\
- __FILE__, __LINE__, __FUNCTION__)
-
-using namespace std::string_literals;
+#include "nativebridge/native_bridge.h"
+#include "nativehelper/ScopedUtfChars.h"
namespace android {
+namespace {
#if defined(__ANDROID__)
-struct NativeLoaderNamespace {
- public:
- NativeLoaderNamespace()
- : android_ns_(nullptr), native_bridge_ns_(nullptr) { }
+constexpr const char* kApexPath = "/apex/";
- explicit NativeLoaderNamespace(android_namespace_t* ns)
- : android_ns_(ns), native_bridge_ns_(nullptr) { }
+std::mutex g_namespaces_mutex;
+LibraryNamespaces* g_namespaces = new LibraryNamespaces;
- explicit NativeLoaderNamespace(native_bridge_namespace_t* ns)
- : android_ns_(nullptr), native_bridge_ns_(ns) { }
-
- NativeLoaderNamespace(NativeLoaderNamespace&& that) = default;
- NativeLoaderNamespace(const NativeLoaderNamespace& that) = default;
-
- NativeLoaderNamespace& operator=(const NativeLoaderNamespace& that) = default;
-
- android_namespace_t* get_android_ns() const {
- CHECK(native_bridge_ns_ == nullptr);
- return android_ns_;
+android_namespace_t* FindExportedNamespace(const char* caller_location) {
+ std::string location = caller_location;
+ // Lots of implicit assumptions here: we expect `caller_location` to be of the form:
+ // /apex/com.android...modulename/...
+ //
+ // And we extract from it 'modulename', which is the name of the linker namespace.
+ if (android::base::StartsWith(location, kApexPath)) {
+ size_t slash_index = location.find_first_of('/', strlen(kApexPath));
+ LOG_ALWAYS_FATAL_IF((slash_index == std::string::npos),
+ "Error finding namespace of apex: no slash in path %s", caller_location);
+ size_t dot_index = location.find_last_of('.', slash_index);
+ LOG_ALWAYS_FATAL_IF((dot_index == std::string::npos),
+ "Error finding namespace of apex: no dot in apex name %s", caller_location);
+ std::string name = location.substr(dot_index + 1, slash_index - dot_index - 1);
+ android_namespace_t* boot_namespace = android_get_exported_namespace(name.c_str());
+ LOG_ALWAYS_FATAL_IF((boot_namespace == nullptr),
+ "Error finding namespace of apex: no namespace called %s", name.c_str());
+ return boot_namespace;
}
-
- native_bridge_namespace_t* get_native_bridge_ns() const {
- CHECK(android_ns_ == nullptr);
- return native_bridge_ns_;
- }
-
- bool is_android_namespace() const {
- return native_bridge_ns_ == nullptr;
- }
-
- private:
- // Only one of them can be not null
- android_namespace_t* android_ns_;
- native_bridge_namespace_t* native_bridge_ns_;
-};
-
-static constexpr const char kPublicNativeLibrariesSystemConfigPathFromRoot[] =
- "/etc/public.libraries.txt";
-static constexpr const char kPublicNativeLibrariesExtensionConfigPrefix[] = "public.libraries-";
-static constexpr const size_t kPublicNativeLibrariesExtensionConfigPrefixLen =
- sizeof(kPublicNativeLibrariesExtensionConfigPrefix) - 1;
-static constexpr const char kPublicNativeLibrariesExtensionConfigSuffix[] = ".txt";
-static constexpr const size_t kPublicNativeLibrariesExtensionConfigSuffixLen =
- sizeof(kPublicNativeLibrariesExtensionConfigSuffix) - 1;
-static constexpr const char kPublicNativeLibrariesVendorConfig[] =
- "/vendor/etc/public.libraries.txt";
-static constexpr const char kLlndkNativeLibrariesSystemConfigPathFromRoot[] =
- "/etc/llndk.libraries.txt";
-static constexpr const char kVndkspNativeLibrariesSystemConfigPathFromRoot[] =
- "/etc/vndksp.libraries.txt";
-
-static const std::vector<const std::string> kRuntimePublicLibraries = {
- "libicuuc.so",
- "libicui18n.so",
-};
-
-// The device may be configured to have the vendor libraries loaded to a separate namespace.
-// For historical reasons this namespace was named sphal but effectively it is intended
-// to use to load vendor libraries to separate namespace with controlled interface between
-// vendor and system namespaces.
-static constexpr const char* kVendorNamespaceName = "sphal";
-
-static constexpr const char* kVndkNamespaceName = "vndk";
-
-static constexpr const char* kDefaultNamespaceName = "default";
-static constexpr const char* kPlatformNamespaceName = "platform";
-static constexpr const char* kRuntimeNamespaceName = "runtime";
-
-// classloader-namespace is a linker namespace that is created for the loaded
-// app. To be specific, it is created for the app classloader. When
-// System.load() is called from a Java class that is loaded from the
-// classloader, the classloader-namespace namespace associated with that
-// classloader is selected for dlopen. The namespace is configured so that its
-// search path is set to the app-local JNI directory and it is linked to the
-// default namespace with the names of libs listed in the public.libraries.txt.
-// This way an app can only load its own JNI libraries along with the public libs.
-static constexpr const char* kClassloaderNamespaceName = "classloader-namespace";
-// Same thing for vendor APKs.
-static constexpr const char* kVendorClassloaderNamespaceName = "vendor-classloader-namespace";
-
-// (http://b/27588281) This is a workaround for apps using custom classloaders and calling
-// System.load() with an absolute path which is outside of the classloader library search path.
-// This list includes all directories app is allowed to access this way.
-static constexpr const char* kWhitelistedDirectories = "/data:/mnt/expand";
-
-static constexpr const char* kApexPath = "/apex/";
-
-#if defined(__LP64__)
-static constexpr const char* kRuntimeApexLibPath = "/apex/com.android.runtime/lib64";
-static constexpr const char* kVendorLibPath = "/vendor/lib64";
-static constexpr const char* kProductLibPath = "/product/lib64:/system/product/lib64";
-#else
-static constexpr const char* kRuntimeApexLibPath = "/apex/com.android.runtime/lib";
-static constexpr const char* kVendorLibPath = "/vendor/lib";
-static constexpr const char* kProductLibPath = "/product/lib:/system/product/lib";
-#endif
-
-static const std::regex kVendorDexPathRegex("(^|:)/vendor/");
-static const std::regex kProductDexPathRegex("(^|:)(/system)?/product/");
-
-// Define origin of APK if it is from vendor partition or product partition
-typedef enum {
- APK_ORIGIN_DEFAULT = 0,
- APK_ORIGIN_VENDOR = 1,
- APK_ORIGIN_PRODUCT = 2,
-} ApkOrigin;
-
-static bool is_debuggable() {
- bool debuggable = false;
-#ifdef __BIONIC__
- debuggable = android::base::GetBoolProperty("ro.debuggable", false);
-#endif
- return debuggable;
+ return nullptr;
}
-
-static std::string vndk_version_str() {
-#ifdef __BIONIC__
- std::string version = android::base::GetProperty("ro.vndk.version", "");
- if (version != "" && version != "current") {
- return "." + version;
- }
-#endif
- return "";
-}
-
-static void insert_vndk_version_str(std::string* file_name) {
- CHECK(file_name != nullptr);
- size_t insert_pos = file_name->find_last_of(".");
- if (insert_pos == std::string::npos) {
- insert_pos = file_name->length();
- }
- file_name->insert(insert_pos, vndk_version_str());
-}
-
-static const std::function<bool(const std::string&, std::string*)> always_true =
- [](const std::string&, std::string*) { return true; };
-
-class LibraryNamespaces {
- public:
- LibraryNamespaces() : initialized_(false) { }
-
- NativeLoaderNamespace* Create(JNIEnv* env, uint32_t target_sdk_version, jobject class_loader,
- bool is_shared, jstring dex_path, jstring java_library_path,
- jstring java_permitted_path, std::string* error_msg) {
- std::string library_path; // empty string by default.
-
- if (java_library_path != nullptr) {
- ScopedUtfChars library_path_utf_chars(env, java_library_path);
- library_path = library_path_utf_chars.c_str();
- }
-
- ApkOrigin apk_origin = GetApkOriginFromDexPath(env, dex_path);
-
- // (http://b/27588281) This is a workaround for apps using custom
- // classloaders and calling System.load() with an absolute path which
- // is outside of the classloader library search path.
- //
- // This part effectively allows such a classloader to access anything
- // under /data and /mnt/expand
- std::string permitted_path = kWhitelistedDirectories;
-
- if (java_permitted_path != nullptr) {
- ScopedUtfChars path(env, java_permitted_path);
- if (path.c_str() != nullptr && path.size() > 0) {
- permitted_path = permitted_path + ":" + path.c_str();
- }
- }
-
- if (!initialized_ && !InitPublicNamespace(library_path.c_str(), error_msg)) {
- return nullptr;
- }
-
- bool found = FindNamespaceByClassLoader(env, class_loader);
-
- LOG_ALWAYS_FATAL_IF(found,
- "There is already a namespace associated with this classloader");
-
- uint64_t namespace_type = ANDROID_NAMESPACE_TYPE_ISOLATED;
- if (is_shared) {
- namespace_type |= ANDROID_NAMESPACE_TYPE_SHARED;
- }
-
- if (target_sdk_version < 24) {
- namespace_type |= ANDROID_NAMESPACE_TYPE_GREYLIST_ENABLED;
- }
-
- NativeLoaderNamespace* parent_ns = FindParentNamespaceByClassLoader(env, class_loader);
-
- bool is_native_bridge = false;
-
- if (parent_ns != nullptr) {
- is_native_bridge = !parent_ns->is_android_namespace();
- } else if (!library_path.empty()) {
- is_native_bridge = NativeBridgeIsPathSupported(library_path.c_str());
- }
-
- std::string system_exposed_libraries = system_public_libraries_;
- const char* namespace_name = kClassloaderNamespaceName;
- android_namespace_t* vndk_ns = nullptr;
- if ((apk_origin == APK_ORIGIN_VENDOR ||
- (apk_origin == APK_ORIGIN_PRODUCT && target_sdk_version > 29)) &&
- !is_shared) {
- LOG_FATAL_IF(is_native_bridge,
- "Unbundled vendor / product apk must not use translated architecture");
-
- // For vendor / product apks, give access to the vendor / product lib even though
- // they are treated as unbundled; the libs and apks are still bundled
- // together in the vendor / product partition.
- const char* origin_partition;
- const char* origin_lib_path;
-
- switch (apk_origin) {
- case APK_ORIGIN_VENDOR:
- origin_partition = "vendor";
- origin_lib_path = kVendorLibPath;
- break;
- case APK_ORIGIN_PRODUCT:
- origin_partition = "product";
- origin_lib_path = kProductLibPath;
- break;
- default:
- origin_partition = "unknown";
- origin_lib_path = "";
- }
-
- LOG_FATAL_IF(is_native_bridge, "Unbundled %s apk must not use translated architecture",
- origin_partition);
-
- library_path = library_path + ":" + origin_lib_path;
- permitted_path = permitted_path + ":" + origin_lib_path;
-
- // Also give access to LLNDK libraries since they are available to vendors
- system_exposed_libraries = system_exposed_libraries + ":" + system_llndk_libraries_.c_str();
-
- // Give access to VNDK-SP libraries from the 'vndk' namespace.
- vndk_ns = android_get_exported_namespace(kVndkNamespaceName);
- if (vndk_ns == nullptr) {
- ALOGW("Cannot find \"%s\" namespace for %s apks", kVndkNamespaceName, origin_partition);
- }
-
- // Different name is useful for debugging
- namespace_name = kVendorClassloaderNamespaceName;
- ALOGD("classloader namespace configured for unbundled %s apk. library_path=%s",
- origin_partition, library_path.c_str());
- } else {
- // oem and product public libraries are NOT available to vendor apks, otherwise it
- // would be system->vendor violation.
- if (!oem_public_libraries_.empty()) {
- system_exposed_libraries = system_exposed_libraries + ':' + oem_public_libraries_;
- }
- if (!product_public_libraries_.empty()) {
- system_exposed_libraries = system_exposed_libraries + ':' + product_public_libraries_;
- }
- }
-
- std::string runtime_exposed_libraries = base::Join(kRuntimePublicLibraries, ":");
-
- NativeLoaderNamespace native_loader_ns;
- if (!is_native_bridge) {
- android_namespace_t* android_parent_ns;
- if (parent_ns != nullptr) {
- android_parent_ns = parent_ns->get_android_ns();
- } else {
- // Fall back to the platform namespace if no parent is found. It is
- // called "default" for binaries in /system and "platform" for those in
- // the Runtime APEX. Try "platform" first since "default" always exists.
- android_parent_ns = android_get_exported_namespace(kPlatformNamespaceName);
- if (android_parent_ns == nullptr) {
- android_parent_ns = android_get_exported_namespace(kDefaultNamespaceName);
- }
- }
-
- android_namespace_t* ns = android_create_namespace(namespace_name,
- nullptr,
- library_path.c_str(),
- namespace_type,
- permitted_path.c_str(),
- android_parent_ns);
- if (ns == nullptr) {
- *error_msg = dlerror();
- return nullptr;
- }
-
- // Note that when vendor_ns is not configured this function will return nullptr
- // and it will result in linking vendor_public_libraries_ to the default namespace
- // which is expected behavior in this case.
- android_namespace_t* vendor_ns = android_get_exported_namespace(kVendorNamespaceName);
-
- android_namespace_t* runtime_ns = android_get_exported_namespace(kRuntimeNamespaceName);
-
- if (!android_link_namespaces(ns, nullptr, system_exposed_libraries.c_str())) {
- *error_msg = dlerror();
- return nullptr;
- }
-
- // Runtime apex does not exist in host, and under certain build conditions.
- if (runtime_ns != nullptr) {
- if (!android_link_namespaces(ns, runtime_ns, runtime_exposed_libraries.c_str())) {
- *error_msg = dlerror();
- return nullptr;
- }
- }
-
- if (vndk_ns != nullptr && !system_vndksp_libraries_.empty()) {
- // vendor apks are allowed to use VNDK-SP libraries.
- if (!android_link_namespaces(ns, vndk_ns, system_vndksp_libraries_.c_str())) {
- *error_msg = dlerror();
- return nullptr;
- }
- }
-
- if (!vendor_public_libraries_.empty()) {
- if (!android_link_namespaces(ns, vendor_ns, vendor_public_libraries_.c_str())) {
- *error_msg = dlerror();
- return nullptr;
- }
- }
-
- native_loader_ns = NativeLoaderNamespace(ns);
- } else {
- native_bridge_namespace_t* native_bridge_parent_namespace;
- if (parent_ns != nullptr) {
- native_bridge_parent_namespace = parent_ns->get_native_bridge_ns();
- } else {
- native_bridge_parent_namespace = NativeBridgeGetExportedNamespace(kPlatformNamespaceName);
- if (native_bridge_parent_namespace == nullptr) {
- native_bridge_parent_namespace = NativeBridgeGetExportedNamespace(kDefaultNamespaceName);
- }
- }
-
- native_bridge_namespace_t* ns = NativeBridgeCreateNamespace(namespace_name,
- nullptr,
- library_path.c_str(),
- namespace_type,
- permitted_path.c_str(),
- native_bridge_parent_namespace);
- if (ns == nullptr) {
- *error_msg = NativeBridgeGetError();
- return nullptr;
- }
-
- native_bridge_namespace_t* vendor_ns = NativeBridgeGetExportedNamespace(kVendorNamespaceName);
- native_bridge_namespace_t* runtime_ns =
- NativeBridgeGetExportedNamespace(kRuntimeNamespaceName);
-
- if (!NativeBridgeLinkNamespaces(ns, nullptr, system_exposed_libraries.c_str())) {
- *error_msg = NativeBridgeGetError();
- return nullptr;
- }
-
- // Runtime apex does not exist in host, and under certain build conditions.
- if (runtime_ns != nullptr) {
- if (!NativeBridgeLinkNamespaces(ns, runtime_ns, runtime_exposed_libraries.c_str())) {
- *error_msg = NativeBridgeGetError();
- return nullptr;
- }
- }
- if (!vendor_public_libraries_.empty()) {
- if (!NativeBridgeLinkNamespaces(ns, vendor_ns, vendor_public_libraries_.c_str())) {
- *error_msg = NativeBridgeGetError();
- return nullptr;
- }
- }
-
- native_loader_ns = NativeLoaderNamespace(ns);
- }
-
- namespaces_.push_back(std::make_pair(env->NewWeakGlobalRef(class_loader), native_loader_ns));
-
- return &(namespaces_.back().second);
- }
-
- NativeLoaderNamespace* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
- auto it = std::find_if(namespaces_.begin(), namespaces_.end(),
- [&](const std::pair<jweak, NativeLoaderNamespace>& value) {
- return env->IsSameObject(value.first, class_loader);
- });
- if (it != namespaces_.end()) {
- return &it->second;
- }
-
- return nullptr;
- }
-
- void Initialize() {
- // Once public namespace is initialized there is no
- // point in running this code - it will have no effect
- // on the current list of public libraries.
- if (initialized_) {
- return;
- }
-
- std::vector<std::string> sonames;
- const char* android_root_env = getenv("ANDROID_ROOT");
- std::string root_dir = android_root_env != nullptr ? android_root_env : "/system";
- std::string public_native_libraries_system_config =
- root_dir + kPublicNativeLibrariesSystemConfigPathFromRoot;
- std::string llndk_native_libraries_system_config =
- root_dir + kLlndkNativeLibrariesSystemConfigPathFromRoot;
- std::string vndksp_native_libraries_system_config =
- root_dir + kVndkspNativeLibrariesSystemConfigPathFromRoot;
-
- std::string product_public_native_libraries_dir = "/product/etc";
-
- std::string error_msg;
- LOG_ALWAYS_FATAL_IF(
- !ReadConfig(public_native_libraries_system_config, &sonames, always_true, &error_msg),
- "Error reading public native library list from \"%s\": %s",
- public_native_libraries_system_config.c_str(), error_msg.c_str());
-
- // For debuggable platform builds use ANDROID_ADDITIONAL_PUBLIC_LIBRARIES environment
- // variable to add libraries to the list. This is intended for platform tests only.
- if (is_debuggable()) {
- const char* additional_libs = getenv("ANDROID_ADDITIONAL_PUBLIC_LIBRARIES");
- if (additional_libs != nullptr && additional_libs[0] != '\0') {
- std::vector<std::string> additional_libs_vector = base::Split(additional_libs, ":");
- std::copy(additional_libs_vector.begin(), additional_libs_vector.end(),
- std::back_inserter(sonames));
- }
- }
-
- // Remove the public libs in the runtime namespace.
- // These libs are listed in public.android.txt, but we don't want the rest of android
- // in default namespace to dlopen the libs.
- // For example, libicuuc.so is exposed to classloader namespace from runtime namespace.
- // Unfortunately, it does not have stable C symbols, and default namespace should only use
- // stable symbols in libandroidicu.so. http://b/120786417
- removePublicLibsIfExistsInRuntimeApex(sonames);
-
- // android_init_namespaces() expects all the public libraries
- // to be loaded so that they can be found by soname alone.
- //
- // TODO(dimitry): this is a bit misleading since we do not know
- // if the vendor public library is going to be opened from /vendor/lib
- // we might as well end up loading them from /system/lib or /product/lib
- // For now we rely on CTS test to catch things like this but
- // it should probably be addressed in the future.
- for (const auto& soname : sonames) {
- LOG_ALWAYS_FATAL_IF(dlopen(soname.c_str(), RTLD_NOW | RTLD_NODELETE) == nullptr,
- "Error preloading public library %s: %s", soname.c_str(), dlerror());
- }
-
- system_public_libraries_ = base::Join(sonames, ':');
-
- // read /system/etc/public.libraries-<companyname>.txt which contain partner defined
- // system libs that are exposed to apps. The libs in the txt files must be
- // named as lib<name>.<companyname>.so.
- sonames.clear();
- ReadExtensionLibraries(base::Dirname(public_native_libraries_system_config).c_str(), &sonames);
- oem_public_libraries_ = base::Join(sonames, ':');
-
- // read /product/etc/public.libraries-<companyname>.txt which contain partner defined
- // product libs that are exposed to apps.
- sonames.clear();
- ReadExtensionLibraries(product_public_native_libraries_dir.c_str(), &sonames);
- product_public_libraries_ = base::Join(sonames, ':');
-
- // Insert VNDK version to llndk and vndksp config file names.
- insert_vndk_version_str(&llndk_native_libraries_system_config);
- insert_vndk_version_str(&vndksp_native_libraries_system_config);
-
- sonames.clear();
- ReadConfig(llndk_native_libraries_system_config, &sonames, always_true);
- system_llndk_libraries_ = base::Join(sonames, ':');
-
- sonames.clear();
- ReadConfig(vndksp_native_libraries_system_config, &sonames, always_true);
- system_vndksp_libraries_ = base::Join(sonames, ':');
-
- sonames.clear();
- // This file is optional, quietly ignore if the file does not exist.
- ReadConfig(kPublicNativeLibrariesVendorConfig, &sonames, always_true, nullptr);
-
- vendor_public_libraries_ = base::Join(sonames, ':');
- }
-
- void Reset() { namespaces_.clear(); }
-
- private:
- void ReadExtensionLibraries(const char* dirname, std::vector<std::string>* sonames) {
- std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(dirname), closedir);
- if (dir != nullptr) {
- // Failing to opening the dir is not an error, which can happen in
- // webview_zygote.
- while (struct dirent* ent = readdir(dir.get())) {
- if (ent->d_type != DT_REG && ent->d_type != DT_LNK) {
- continue;
- }
- const std::string filename(ent->d_name);
- if (android::base::StartsWith(filename, kPublicNativeLibrariesExtensionConfigPrefix) &&
- android::base::EndsWith(filename, kPublicNativeLibrariesExtensionConfigSuffix)) {
- const size_t start = kPublicNativeLibrariesExtensionConfigPrefixLen;
- const size_t end = filename.size() - kPublicNativeLibrariesExtensionConfigSuffixLen;
- const std::string company_name = filename.substr(start, end - start);
- const std::string config_file_path = dirname + "/"s + filename;
- LOG_ALWAYS_FATAL_IF(
- company_name.empty(),
- "Error extracting company name from public native library list file path \"%s\"",
- config_file_path.c_str());
-
- std::string error_msg;
-
- LOG_ALWAYS_FATAL_IF(
- !ReadConfig(
- config_file_path, sonames,
- [&company_name](const std::string& soname, std::string* error_msg) {
- if (android::base::StartsWith(soname, "lib") &&
- android::base::EndsWith(soname, "." + company_name + ".so")) {
- return true;
- } else {
- *error_msg = "Library name \"" + soname +
- "\" does not end with the company name: " + company_name + ".";
- return false;
- }
- },
- &error_msg),
- "Error reading public native library list from \"%s\": %s", config_file_path.c_str(),
- error_msg.c_str());
- }
- }
- }
- }
-
- /**
- * Remove the public libs in runtime namespace
- */
- void removePublicLibsIfExistsInRuntimeApex(std::vector<std::string>& sonames) {
- for (const std::string& lib_name : kRuntimePublicLibraries) {
- std::string path(kRuntimeApexLibPath);
- path.append("/").append(lib_name);
-
- struct stat s;
- // Do nothing if the path in /apex does not exist.
- // Runtime APEX must be mounted since libnativeloader is in the same APEX
- if (stat(path.c_str(), &s) != 0) {
- continue;
- }
-
- auto it = std::find(sonames.begin(), sonames.end(), lib_name);
- if (it != sonames.end()) {
- sonames.erase(it);
- }
- }
- }
-
- bool ReadConfig(const std::string& configFile, std::vector<std::string>* sonames,
- const std::function<bool(const std::string& /* soname */,
- std::string* /* error_msg */)>& check_soname,
- std::string* error_msg = nullptr) {
- // Read list of public native libraries from the config file.
- std::string file_content;
- if(!base::ReadFileToString(configFile, &file_content)) {
- if (error_msg) *error_msg = strerror(errno);
- return false;
- }
-
- std::vector<std::string> lines = base::Split(file_content, "\n");
-
- for (auto& line : lines) {
- auto trimmed_line = base::Trim(line);
- if (trimmed_line[0] == '#' || trimmed_line.empty()) {
- continue;
- }
- size_t space_pos = trimmed_line.rfind(' ');
- if (space_pos != std::string::npos) {
- std::string type = trimmed_line.substr(space_pos + 1);
- if (type != "32" && type != "64") {
- if (error_msg) *error_msg = "Malformed line: " + line;
- return false;
- }
-#if defined(__LP64__)
- // Skip 32 bit public library.
- if (type == "32") {
- continue;
- }
-#else
- // Skip 64 bit public library.
- if (type == "64") {
- continue;
- }
-#endif
- trimmed_line.resize(space_pos);
- }
-
- if (check_soname(trimmed_line, error_msg)) {
- sonames->push_back(trimmed_line);
- } else {
- return false;
- }
- }
-
- return true;
- }
-
- bool InitPublicNamespace(const char* library_path, std::string* error_msg) {
- // Ask native bride if this apps library path should be handled by it
- bool is_native_bridge = NativeBridgeIsPathSupported(library_path);
-
- // (http://b/25844435) - Some apps call dlopen from generated code (mono jited
- // code is one example) unknown to linker in which case linker uses anonymous
- // namespace. The second argument specifies the search path for the anonymous
- // namespace which is the library_path of the classloader.
- initialized_ = android_init_anonymous_namespace(system_public_libraries_.c_str(),
- is_native_bridge ? nullptr : library_path);
- if (!initialized_) {
- *error_msg = dlerror();
- return false;
- }
-
- // and now initialize native bridge namespaces if necessary.
- if (NativeBridgeInitialized()) {
- initialized_ = NativeBridgeInitAnonymousNamespace(system_public_libraries_.c_str(),
- is_native_bridge ? library_path : nullptr);
- if (!initialized_) {
- *error_msg = NativeBridgeGetError();
- }
- }
-
- return initialized_;
- }
-
- jobject GetParentClassLoader(JNIEnv* env, jobject class_loader) {
- jclass class_loader_class = env->FindClass("java/lang/ClassLoader");
- jmethodID get_parent = env->GetMethodID(class_loader_class,
- "getParent",
- "()Ljava/lang/ClassLoader;");
-
- return env->CallObjectMethod(class_loader, get_parent);
- }
-
- NativeLoaderNamespace* FindParentNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
- jobject parent_class_loader = GetParentClassLoader(env, class_loader);
-
- while (parent_class_loader != nullptr) {
- NativeLoaderNamespace* ns;
- if ((ns = FindNamespaceByClassLoader(env, parent_class_loader)) != nullptr) {
- return ns;
- }
-
- parent_class_loader = GetParentClassLoader(env, parent_class_loader);
- }
-
- return nullptr;
- }
-
- ApkOrigin GetApkOriginFromDexPath(JNIEnv* env, jstring dex_path) {
- ApkOrigin apk_origin = APK_ORIGIN_DEFAULT;
-
- if (dex_path != nullptr) {
- ScopedUtfChars dex_path_utf_chars(env, dex_path);
-
- if (std::regex_search(dex_path_utf_chars.c_str(), kVendorDexPathRegex)) {
- apk_origin = APK_ORIGIN_VENDOR;
- }
-
- if (std::regex_search(dex_path_utf_chars.c_str(), kProductDexPathRegex)) {
- LOG_ALWAYS_FATAL_IF(apk_origin == APK_ORIGIN_VENDOR,
- "Dex path contains both vendor and product partition : %s",
- dex_path_utf_chars.c_str());
-
- apk_origin = APK_ORIGIN_PRODUCT;
- }
- }
-
- return apk_origin;
- }
-
- bool initialized_;
- std::list<std::pair<jweak, NativeLoaderNamespace>> namespaces_;
- std::string system_public_libraries_;
- std::string vendor_public_libraries_;
- std::string oem_public_libraries_;
- std::string product_public_libraries_;
- std::string system_llndk_libraries_;
- std::string system_vndksp_libraries_;
-
- DISALLOW_COPY_AND_ASSIGN(LibraryNamespaces);
-};
-
-static std::mutex g_namespaces_mutex;
-static LibraryNamespaces* g_namespaces = new LibraryNamespaces;
-#endif
+#endif // #if defined(__ANDROID__)
+} // namespace
void InitializeNativeLoader() {
#if defined(__ANDROID__)
@@ -767,30 +101,6 @@
return nullptr;
}
-#if defined(__ANDROID__)
-static android_namespace_t* FindExportedNamespace(const char* caller_location) {
- std::string location = caller_location;
- // Lots of implicit assumptions here: we expect `caller_location` to be of the form:
- // /apex/com.android...modulename/...
- //
- // And we extract from it 'modulename', which is the name of the linker namespace.
- if (android::base::StartsWith(location, kApexPath)) {
- size_t slash_index = location.find_first_of('/', strlen(kApexPath));
- LOG_ALWAYS_FATAL_IF((slash_index == std::string::npos),
- "Error finding namespace of apex: no slash in path %s", caller_location);
- size_t dot_index = location.find_last_of('.', slash_index);
- LOG_ALWAYS_FATAL_IF((dot_index == std::string::npos),
- "Error finding namespace of apex: no dot in apex name %s", caller_location);
- std::string name = location.substr(dot_index + 1, slash_index - dot_index - 1);
- android_namespace_t* boot_namespace = android_get_exported_namespace(name.c_str());
- LOG_ALWAYS_FATAL_IF((boot_namespace == nullptr),
- "Error finding namespace of apex: no namespace called %s", name.c_str());
- return boot_namespace;
- }
- return nullptr;
-}
-#endif
-
void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* path,
jobject class_loader, const char* caller_location, jstring library_path,
bool* needs_native_bridge, char** error_msg) {
@@ -941,10 +251,11 @@
return nullptr;
}
+
NativeLoaderNamespace* FindNativeLoaderNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
std::lock_guard<std::mutex> guard(g_namespaces_mutex);
return g_namespaces->FindNamespaceByClassLoader(env, class_loader);
}
#endif
-}; // android namespace
+}; // namespace android
diff --git a/libnativeloader/native_loader_namespace.h b/libnativeloader/native_loader_namespace.h
new file mode 100644
index 0000000..71b60d8
--- /dev/null
+++ b/libnativeloader/native_loader_namespace.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2019 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
+#if defined(__ANDROID__)
+
+#include <dlfcn.h>
+
+#include "android-base/logging.h"
+#include "android/dlext.h"
+#include "log/log.h"
+#include "nativebridge/native_bridge.h"
+
+namespace android {
+
+// NativeLoaderNamespace abstracts a linker namespace for the native
+// architecture (ex: arm on arm) or the translated architecture (ex: arm on
+// x86). Instances of this class are managed by LibraryNamespaces object.
+struct NativeLoaderNamespace {
+ public:
+ NativeLoaderNamespace() : android_ns_(nullptr), native_bridge_ns_(nullptr) {}
+
+ explicit NativeLoaderNamespace(android_namespace_t* ns)
+ : android_ns_(ns), native_bridge_ns_(nullptr) {}
+
+ explicit NativeLoaderNamespace(native_bridge_namespace_t* ns)
+ : android_ns_(nullptr), native_bridge_ns_(ns) {}
+
+ NativeLoaderNamespace(NativeLoaderNamespace&&) = default;
+ NativeLoaderNamespace(const NativeLoaderNamespace&) = default;
+ NativeLoaderNamespace& operator=(const NativeLoaderNamespace&) = default;
+
+ android_namespace_t* get_android_ns() const {
+ CHECK(native_bridge_ns_ == nullptr);
+ return android_ns_;
+ }
+
+ native_bridge_namespace_t* get_native_bridge_ns() const {
+ CHECK(android_ns_ == nullptr);
+ return native_bridge_ns_;
+ }
+
+ bool is_android_namespace() const { return native_bridge_ns_ == nullptr; }
+
+ private:
+ // Only one of them can be not null
+ android_namespace_t* android_ns_;
+ native_bridge_namespace_t* native_bridge_ns_;
+};
+
+} // namespace android
+#endif // #if defined(__ANDROID__)
diff --git a/libsparse/sparse.cpp b/libsparse/sparse.cpp
index cb288c5..24c6379 100644
--- a/libsparse/sparse.cpp
+++ b/libsparse/sparse.cpp
@@ -188,7 +188,7 @@
int (*write)(void* priv, const void* data, size_t len,
unsigned int block, unsigned int nr_blocks),
void* priv) {
- int ret;
+ int ret = 0;
int chunks;
struct chunk_data chk;
struct output_file* out;
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 26626b5..37323dc 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -25,6 +25,7 @@
#include <algorithm>
#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <demangle.h>
@@ -168,7 +169,7 @@
// If this elf is memory backed, and there is a valid file, then set
// an indicator that we couldn't open the file.
if (!elf_from_memory_not_file_ && map_info->memory_backed_elf && !map_info->name.empty() &&
- map_info->name[0] != '[') {
+ map_info->name[0] != '[' && !android::base::StartsWith(map_info->name, "/memfd:")) {
elf_from_memory_not_file_ = true;
}
step_pc = regs_->pc();
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index f635021..30e57a1 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -126,6 +126,12 @@
const auto& info5 = *--maps_->end();
info5->memory_backed_elf = true;
+ elf = new ElfFake(new MemoryFake);
+ elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+ AddMapInfo(0xc3000, 0xc4000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/memfd:/jit-cache", elf);
+ const auto& info6 = *--maps_->end();
+ info6->memory_backed_elf = true;
+
process_memory_.reset(new MemoryFake);
}
@@ -1234,6 +1240,36 @@
EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
}
+TEST_F(UnwinderTest, elf_from_memory_but_from_memfd) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+
+ regs_.set_pc(0xc3050);
+ regs_.set_sp(0x10000);
+ ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
+ unwinder.Unwind();
+ EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+ ASSERT_EQ(1U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0x50U, frame->rel_pc);
+ EXPECT_EQ(0xc3050U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("Frame0", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/memfd:/jit-cache", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0xc3000U, frame->map_start);
+ EXPECT_EQ(0xc4000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+}
+
// Verify format frame code.
TEST_F(UnwinderTest, format_frame) {
RegsFake regs_arm(10);
diff --git a/libziparchive/include/ziparchive/zip_archive.h b/libziparchive/include/ziparchive/zip_archive.h
index ab38dfd..32c6ea8 100644
--- a/libziparchive/include/ziparchive/zip_archive.h
+++ b/libziparchive/include/ziparchive/zip_archive.h
@@ -25,6 +25,8 @@
#include <sys/cdefs.h>
#include <sys/types.h>
+#include <string_view>
+
#include "android-base/off64_t.h"
/* Zip compression methods we support */
@@ -39,10 +41,7 @@
ZipString() {}
- /*
- * entry_name has to be an c-style string with only ASCII characters.
- */
- explicit ZipString(const char* entry_name);
+ explicit ZipString(std::string_view entry_name);
bool operator==(const ZipString& rhs) const {
return name && (name_length == rhs.name_length) && (memcmp(name, rhs.name, name_length) == 0);
@@ -149,8 +148,7 @@
void CloseArchive(ZipArchiveHandle archive);
/*
- * Find an entry in the Zip archive, by name. |entryName| must be a null
- * terminated string, and |data| must point to a writeable memory location.
+ * Find an entry in the Zip archive, by name. |data| must be non-null.
*
* Returns 0 if an entry is found, and populates |data| with information
* about this entry. Returns negative values otherwise.
@@ -164,6 +162,8 @@
* On non-Windows platforms this method does not modify internal state and
* can be called concurrently.
*/
+int32_t FindEntry(const ZipArchiveHandle archive, const std::string_view entryName, ZipEntry* data);
+// TODO: remove this internally, where there is a new user.
int32_t FindEntry(const ZipArchiveHandle archive, const ZipString& entryName, ZipEntry* data);
/*
@@ -179,6 +179,7 @@
*
* Returns 0 on success and negative values on failure.
*/
+// TODO: switch these ZipStrings to std::string_view.
int32_t StartIteration(ZipArchiveHandle archive, void** cookie_ptr,
const ZipString* optional_prefix, const ZipString* optional_suffix);
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 596786a..bc7103b 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -690,8 +690,7 @@
struct IterationHandle {
uint32_t position;
- // We're not using vector here because this code is used in the Windows SDK
- // where the STL is not available.
+ // TODO: switch these to std::string now that Windows uses libc++ too.
ZipString prefix;
ZipString suffix;
ZipArchive* archive;
@@ -742,6 +741,7 @@
delete reinterpret_cast<IterationHandle*>(cookie);
}
+// TODO: remove this internally.
int32_t FindEntry(const ZipArchiveHandle archive, const ZipString& entryName, ZipEntry* data) {
if (entryName.name_length == 0) {
ALOGW("Zip: Invalid filename %.*s", entryName.name_length, entryName.name);
@@ -758,6 +758,23 @@
return FindEntry(archive, static_cast<uint32_t>(ent), data);
}
+int32_t FindEntry(const ZipArchiveHandle archive, const std::string_view entryName,
+ ZipEntry* data) {
+ if (entryName.empty() || entryName.size() > static_cast<size_t>(UINT16_MAX)) {
+ ALOGW("Zip: Invalid filename of length %zu", entryName.size());
+ return kInvalidEntryName;
+ }
+
+ const int64_t ent = EntryToIndex(archive->hash_table, archive->hash_table_size,
+ ZipString(entryName), archive->central_directory.GetBasePtr());
+ if (ent < 0) {
+ ALOGV("Zip: Could not find entry %.*s", static_cast<int>(entryName.size()), entryName.data());
+ return static_cast<int32_t>(ent); // kEntryNotFound is safe to truncate.
+ }
+ // We know there are at most hast_table_size entries, safe to truncate.
+ return FindEntry(archive, static_cast<uint32_t>(ent), data);
+}
+
int32_t Next(void* cookie, ZipEntry* data, ZipString* name) {
IterationHandle* handle = reinterpret_cast<IterationHandle*>(cookie);
if (handle == NULL) {
@@ -1152,8 +1169,9 @@
return archive->mapped_zip.GetFileDescriptor();
}
-ZipString::ZipString(const char* entry_name) : name(reinterpret_cast<const uint8_t*>(entry_name)) {
- size_t len = strlen(entry_name);
+ZipString::ZipString(std::string_view entry_name)
+ : name(reinterpret_cast<const uint8_t*>(entry_name.data())) {
+ size_t len = entry_name.size();
CHECK_LE(len, static_cast<size_t>(UINT16_MAX));
name_length = static_cast<uint16_t>(len);
}
diff --git a/libziparchive/zip_archive_benchmark.cpp b/libziparchive/zip_archive_benchmark.cpp
index 46aa5a6..52166a4 100644
--- a/libziparchive/zip_archive_benchmark.cpp
+++ b/libziparchive/zip_archive_benchmark.cpp
@@ -55,7 +55,7 @@
// In order to walk through all file names in the archive, look for a name
// that does not exist in the archive.
- ZipString name("thisFileNameDoesNotExist");
+ std::string_view name("thisFileNameDoesNotExist");
// Start the benchmark.
while (state.KeepRunning()) {
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index e471d5e..cfbce1c 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -64,12 +64,6 @@
return OpenArchive(abs_path.c_str(), handle);
}
-static void SetZipString(ZipString* zip_str, const std::string& str) {
- zip_str->name = reinterpret_cast<const uint8_t*>(str.c_str());
- CHECK_LE(str.size(), std::numeric_limits<uint16_t>::max());
- zip_str->name_length = static_cast<uint16_t>(str.size());
-}
-
TEST(ziparchive, Open) {
ZipArchiveHandle handle;
ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
@@ -192,9 +186,7 @@
ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
ZipEntry data;
- ZipString name;
- SetZipString(&name, kATxtName);
- ASSERT_EQ(0, FindEntry(handle, name, &data));
+ ASSERT_EQ(0, FindEntry(handle, kATxtName, &data));
// Known facts about a.txt, from zipinfo -v.
ASSERT_EQ(63, data.offset);
@@ -205,9 +197,28 @@
ASSERT_EQ(static_cast<uint32_t>(0x438a8005), data.mod_time);
// An entry that doesn't exist. Should be a negative return code.
- ZipString absent_name;
- SetZipString(&absent_name, kNonexistentTxtName);
- ASSERT_LT(FindEntry(handle, absent_name, &data), 0);
+ ASSERT_LT(FindEntry(handle, kNonexistentTxtName, &data), 0);
+
+ CloseArchive(handle);
+}
+
+TEST(ziparchive, FindEntry_empty) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+ ZipEntry data;
+ ASSERT_EQ(kInvalidEntryName, FindEntry(handle, "", &data));
+
+ CloseArchive(handle);
+}
+
+TEST(ziparchive, FindEntry_too_long) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+ std::string very_long_name(65536, 'x');
+ ZipEntry data;
+ ASSERT_EQ(kInvalidEntryName, FindEntry(handle, very_long_name, &data));
CloseArchive(handle);
}
@@ -234,9 +245,7 @@
// An entry that's deflated.
ZipEntry data;
- ZipString a_name;
- SetZipString(&a_name, kATxtName);
- ASSERT_EQ(0, FindEntry(handle, a_name, &data));
+ ASSERT_EQ(0, FindEntry(handle, kATxtName, &data));
const uint32_t a_size = data.uncompressed_length;
ASSERT_EQ(a_size, kATxtContents.size());
uint8_t* buffer = new uint8_t[a_size];
@@ -245,9 +254,7 @@
delete[] buffer;
// An entry that's stored.
- ZipString b_name;
- SetZipString(&b_name, kBTxtName);
- ASSERT_EQ(0, FindEntry(handle, b_name, &data));
+ ASSERT_EQ(0, FindEntry(handle, kBTxtName, &data));
const uint32_t b_size = data.uncompressed_length;
ASSERT_EQ(b_size, kBTxtContents.size());
buffer = new uint8_t[b_size];
@@ -302,9 +309,7 @@
ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle, false));
ZipEntry entry;
- ZipString empty_name;
- SetZipString(&empty_name, kEmptyTxtName);
- ASSERT_EQ(0, FindEntry(handle, empty_name, &entry));
+ ASSERT_EQ(0, FindEntry(handle, kEmptyTxtName, &entry));
ASSERT_EQ(static_cast<uint32_t>(0), entry.uncompressed_length);
uint8_t buffer[1];
ASSERT_EQ(0, ExtractToMemory(handle, &entry, buffer, 1));
@@ -327,9 +332,7 @@
ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EntryLargerThan32KTest", &handle, false));
ZipEntry entry;
- ZipString ab_name;
- SetZipString(&ab_name, kAbTxtName);
- ASSERT_EQ(0, FindEntry(handle, ab_name, &entry));
+ ASSERT_EQ(0, FindEntry(handle, kAbTxtName, &entry));
ASSERT_EQ(kAbUncompressedSize, entry.uncompressed_length);
// Extract the entry to memory.
@@ -386,9 +389,7 @@
ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
ZipEntry entry;
- ZipString name;
- SetZipString(&name, kATxtName);
- ASSERT_EQ(0, FindEntry(handle, name, &entry));
+ ASSERT_EQ(0, FindEntry(handle, kATxtName, &entry));
ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_file.fd));
// Assert that the first 8 bytes of the file haven't been clobbered.
@@ -424,10 +425,8 @@
OpenArchiveFromMemory(file_map->data(), file_map->size(), zip_path.c_str(), &handle));
// Assert one entry can be found and extracted correctly.
- std::string BINARY_PATH("META-INF/com/google/android/update-binary");
- ZipString binary_path(BINARY_PATH.c_str());
ZipEntry binary_entry;
- ASSERT_EQ(0, FindEntry(handle, binary_path, &binary_entry));
+ ASSERT_EQ(0, FindEntry(handle, "META-INF/com/google/android/update-binary", &binary_entry));
TemporaryFile tmp_binary;
ASSERT_NE(-1, tmp_binary.fd);
ASSERT_EQ(0, ExtractEntryToFile(handle, &binary_entry, tmp_binary.fd));
@@ -436,9 +435,7 @@
static void ZipArchiveStreamTest(ZipArchiveHandle& handle, const std::string& entry_name, bool raw,
bool verified, ZipEntry* entry, std::vector<uint8_t>* read_data) {
- ZipString name;
- SetZipString(&name, entry_name);
- ASSERT_EQ(0, FindEntry(handle, name, entry));
+ ASSERT_EQ(0, FindEntry(handle, entry_name, entry));
std::unique_ptr<ZipArchiveStreamEntry> stream;
if (raw) {
stream.reset(ZipArchiveStreamEntry::CreateRaw(handle, *entry));
@@ -589,11 +586,7 @@
// an entry whose name is "name" and whose size is 12 (contents =
// "abdcdefghijk").
ZipEntry entry;
- ZipString name;
- std::string name_str = "name";
- SetZipString(&name, name_str);
-
- ASSERT_EQ(0, FindEntry(handle, name, &entry));
+ ASSERT_EQ(0, FindEntry(handle, "name", &entry));
ASSERT_EQ(static_cast<uint32_t>(12), entry.uncompressed_length);
entry_out->resize(12);
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc
index 63adbbc..c3da23c 100644
--- a/libziparchive/zip_writer_test.cc
+++ b/libziparchive/zip_writer_test.cc
@@ -62,7 +62,7 @@
ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
EXPECT_EQ(kCompressStored, data.method);
EXPECT_EQ(0u, data.has_data_descriptor);
EXPECT_EQ(strlen(expected), data.compressed_length);
@@ -95,19 +95,19 @@
ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
EXPECT_EQ(kCompressStored, data.method);
EXPECT_EQ(2u, data.compressed_length);
ASSERT_EQ(2u, data.uncompressed_length);
ASSERT_TRUE(AssertFileEntryContentsEq("he", handle, &data));
- ASSERT_EQ(0, FindEntry(handle, ZipString("file/file.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "file/file.txt", &data));
EXPECT_EQ(kCompressStored, data.method);
EXPECT_EQ(3u, data.compressed_length);
ASSERT_EQ(3u, data.uncompressed_length);
ASSERT_TRUE(AssertFileEntryContentsEq("llo", handle, &data));
- ASSERT_EQ(0, FindEntry(handle, ZipString("file/file2.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "file/file2.txt", &data));
EXPECT_EQ(kCompressStored, data.method);
EXPECT_EQ(0u, data.compressed_length);
EXPECT_EQ(0u, data.uncompressed_length);
@@ -129,7 +129,7 @@
ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
EXPECT_EQ(0, data.offset & 0x03);
CloseArchive(handle);
@@ -163,7 +163,7 @@
ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
EXPECT_EQ(0, data.offset & 0x03);
struct tm mod = data.GetModificationTime();
@@ -191,7 +191,7 @@
ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
EXPECT_EQ(0, data.offset & 0xfff);
CloseArchive(handle);
@@ -213,7 +213,7 @@
ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
EXPECT_EQ(0, data.offset & 0xfff);
struct tm mod = data.GetModificationTime();
@@ -241,7 +241,7 @@
ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
EXPECT_EQ(kCompressDeflated, data.method);
ASSERT_EQ(4u, data.uncompressed_length);
ASSERT_TRUE(AssertFileEntryContentsEq("helo", handle, &data));
@@ -273,7 +273,7 @@
ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
EXPECT_EQ(kCompressDeflated, data.method);
EXPECT_EQ(kBufSize, data.uncompressed_length);
@@ -340,12 +340,12 @@
ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, ZipString("keep.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "keep.txt", &data));
ASSERT_TRUE(AssertFileEntryContentsEq(kKeepThis, handle, &data));
- ASSERT_NE(0, FindEntry(handle, ZipString("drop.txt"), &data));
+ ASSERT_NE(0, FindEntry(handle, "drop.txt", &data));
- ASSERT_EQ(0, FindEntry(handle, ZipString("replace.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "replace.txt", &data));
ASSERT_TRUE(AssertFileEntryContentsEq(kReplaceWithThis, handle, &data));
CloseArchive(handle);
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 2de7378..48140b8 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -189,8 +189,8 @@
/* vmpressure event handler data */
static struct event_handler_info vmpressure_hinfo[VMPRESS_LEVEL_COUNT];
-/* 3 memory pressure levels, 1 ctrl listen socket, 2 ctrl data socket */
-#define MAX_EPOLL_EVENTS (1 + MAX_DATA_CONN + VMPRESS_LEVEL_COUNT)
+/* 3 memory pressure levels, 1 ctrl listen socket, 2 ctrl data socket, 1 lmk events */
+#define MAX_EPOLL_EVENTS (2 + MAX_DATA_CONN + VMPRESS_LEVEL_COUNT)
static int epollfd;
static int maxevents;
@@ -1863,6 +1863,74 @@
return false;
}
+#ifdef LMKD_LOG_STATS
+static int kernel_poll_fd = -1;
+
+static void poll_kernel() {
+ if (kernel_poll_fd == -1) {
+ // not waiting
+ return;
+ }
+
+ while (1) {
+ char rd_buf[256];
+ int bytes_read =
+ TEMP_FAILURE_RETRY(pread(kernel_poll_fd, (void*)rd_buf, sizeof(rd_buf), 0));
+ if (bytes_read <= 0) break;
+ rd_buf[bytes_read] = '\0';
+
+ int64_t pid;
+ int64_t uid;
+ int64_t group_leader_pid;
+ int64_t min_flt;
+ int64_t maj_flt;
+ int64_t rss_in_pages;
+ int16_t oom_score_adj;
+ int16_t min_score_adj;
+ int64_t starttime;
+ char* taskname = 0;
+ int fields_read = sscanf(rd_buf,
+ "%" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64
+ " %" SCNd64 " %" SCNd16 " %" SCNd16 " %" SCNd64 "\n%m[^\n]",
+ &pid, &uid, &group_leader_pid, &min_flt, &maj_flt, &rss_in_pages,
+ &oom_score_adj, &min_score_adj, &starttime, &taskname);
+
+ /* only the death of the group leader process is logged */
+ if (fields_read == 10 && group_leader_pid == pid) {
+ int64_t process_start_time_ns = starttime * (NS_PER_SEC / sysconf(_SC_CLK_TCK));
+ stats_write_lmk_kill_occurred(log_ctx, LMK_KILL_OCCURRED, uid, taskname, oom_score_adj,
+ min_flt, maj_flt, rss_in_pages * PAGE_SIZE, 0, 0,
+ process_start_time_ns, min_score_adj);
+ }
+
+ free(taskname);
+ }
+}
+
+static struct event_handler_info kernel_poll_hinfo = {0, poll_kernel};
+
+static void init_poll_kernel() {
+ struct epoll_event epev;
+ kernel_poll_fd =
+ TEMP_FAILURE_RETRY(open("/proc/lowmemorykiller", O_RDONLY | O_NONBLOCK | O_CLOEXEC));
+
+ if (kernel_poll_fd < 0) {
+ ALOGE("kernel lmk event file could not be opened; errno=%d", kernel_poll_fd);
+ return;
+ }
+
+ epev.events = EPOLLIN;
+ epev.data.ptr = (void*)&kernel_poll_hinfo;
+ if (epoll_ctl(epollfd, EPOLL_CTL_ADD, kernel_poll_fd, &epev) != 0) {
+ ALOGE("epoll_ctl for lmk events failed; errno=%d", errno);
+ close(kernel_poll_fd);
+ kernel_poll_fd = -1;
+ } else {
+ maxevents++;
+ }
+}
+#endif
+
static int init(void) {
struct epoll_event epev;
int i;
@@ -1910,6 +1978,11 @@
if (use_inkernel_interface) {
ALOGI("Using in-kernel low memory killer interface");
+#ifdef LMKD_LOG_STATS
+ if (enable_stats_log) {
+ init_poll_kernel();
+ }
+#endif
} else {
/* Try to use psi monitor first if kernel has it */
use_psi_monitors = property_get_bool("ro.lmk.use_psi", true) &&
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index a21555c..5a375ec 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -229,70 +229,17 @@
static const char log_warning[] = { KMSG_PRIORITY(LOG_WARNING) };
static const char newline[] = "\n";
- // Dedupe messages, checking for identical messages starting with avc:
- static unsigned count;
- static char* last_str;
- static bool last_info;
+ auditParse(str, uid, &denial_metadata);
+ iov[0].iov_base = info ? const_cast<char*>(log_info) : const_cast<char*>(log_warning);
+ iov[0].iov_len = info ? sizeof(log_info) : sizeof(log_warning);
+ iov[1].iov_base = str;
+ iov[1].iov_len = strlen(str);
+ iov[2].iov_base = const_cast<char*>(denial_metadata.c_str());
+ iov[2].iov_len = denial_metadata.length();
+ iov[3].iov_base = const_cast<char*>(newline);
+ iov[3].iov_len = strlen(newline);
- if (last_str != nullptr) {
- static const char avc[] = "): avc: ";
- char* avcl = strstr(last_str, avc);
- bool skip = false;
-
- if (avcl) {
- char* avcr = strstr(str, avc);
-
- skip = avcr &&
- !fastcmp<strcmp>(avcl + strlen(avc), avcr + strlen(avc));
- if (skip) {
- ++count;
- free(last_str);
- last_str = strdup(str);
- last_info = info;
- }
- }
- if (!skip) {
- static const char resume[] = " duplicate messages suppressed\n";
- iov[0].iov_base = last_info ? const_cast<char*>(log_info)
- : const_cast<char*>(log_warning);
- iov[0].iov_len =
- last_info ? sizeof(log_info) : sizeof(log_warning);
- iov[1].iov_base = last_str;
- iov[1].iov_len = strlen(last_str);
- iov[2].iov_base = const_cast<char*>(denial_metadata.c_str());
- iov[2].iov_len = denial_metadata.length();
- if (count > 1) {
- iov[3].iov_base = const_cast<char*>(resume);
- iov[3].iov_len = strlen(resume);
- } else {
- iov[3].iov_base = const_cast<char*>(newline);
- iov[3].iov_len = strlen(newline);
- }
-
- writev(fdDmesg, iov, arraysize(iov));
- free(last_str);
- last_str = nullptr;
- }
- }
- if (last_str == nullptr) {
- count = 0;
- last_str = strdup(str);
- last_info = info;
- }
- if (count == 0) {
- auditParse(str, uid, &denial_metadata);
- iov[0].iov_base = info ? const_cast<char*>(log_info)
- : const_cast<char*>(log_warning);
- iov[0].iov_len = info ? sizeof(log_info) : sizeof(log_warning);
- iov[1].iov_base = str;
- iov[1].iov_len = strlen(str);
- iov[2].iov_base = const_cast<char*>(denial_metadata.c_str());
- iov[2].iov_len = denial_metadata.length();
- iov[3].iov_base = const_cast<char*>(newline);
- iov[3].iov_len = strlen(newline);
-
- writev(fdDmesg, iov, arraysize(iov));
- }
+ writev(fdDmesg, iov, arraysize(iov));
}
if (!main && !events) {
diff --git a/rootdir/etc/ld.config.legacy.txt b/rootdir/etc/ld.config.legacy.txt
index e081bdf..4b07478 100644
--- a/rootdir/etc/ld.config.legacy.txt
+++ b/rootdir/etc/ld.config.legacy.txt
@@ -20,6 +20,9 @@
[legacy]
namespace.default.isolated = false
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
+namespace.default.visible = true
namespace.default.search.paths = /system/${LIB}
namespace.default.search.paths += /product/${LIB}
@@ -41,7 +44,7 @@
additional.namespaces = runtime,conscrypt,media,resolv
-# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+# Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
# If a shared library or an executable requests a shared library that
# cannot be loaded into the default namespace, the dynamic linker tries
# to load the shared library from the runtime namespace. And then, if the
@@ -50,14 +53,12 @@
# Finally, if all attempts fail, the dynamic linker returns an error.
namespace.default.links = runtime,resolv
namespace.default.asan.links = runtime,resolv
-# Visible because some libraries are dlopen'ed, e.g. libopenjdk is dlopen'ed by
-# libart.
-namespace.default.visible = true
-namespace.default.link.runtime.shared_libs = libdexfile_external.so
+namespace.default.link.runtime.shared_libs = libandroidicu.so
+namespace.default.link.runtime.shared_libs += libdexfile_external.so
+namespace.default.link.runtime.shared_libs += libdexfiled_external.so
namespace.default.link.runtime.shared_libs += libnativebridge.so
namespace.default.link.runtime.shared_libs += libnativehelper.so
namespace.default.link.runtime.shared_libs += libnativeloader.so
-namespace.default.link.runtime.shared_libs += libandroidicu.so
# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
namespace.default.link.runtime.shared_libs += libpac.so
@@ -71,11 +72,13 @@
# "runtime" APEX namespace
#
# This namespace exposes externally accessible libraries from the Runtime APEX.
+# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
###############################################################################
namespace.runtime.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
namespace.runtime.visible = true
-# Keep in sync with ld.config.txt in the com.android.runtime APEX.
namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
namespace.runtime.links = default
@@ -99,7 +102,7 @@
namespace.media.permitted.paths = /apex/com.android.media/${LIB}/extractors
namespace.media.links = default
-namespace.media.link.default.shared_libs += libbinder_ndk.so
+namespace.media.link.default.shared_libs = libbinder_ndk.so
namespace.media.link.default.shared_libs += libc.so
namespace.media.link.default.shared_libs += libcgrouprc.so
namespace.media.link.default.shared_libs += libdl.so
@@ -119,11 +122,11 @@
# "conscrypt" APEX namespace
#
# This namespace is for libraries within the conscrypt APEX.
+# Keep in sync with the "conscrypt" namespace in art/build/apex/ld.config.txt.
###############################################################################
namespace.conscrypt.isolated = true
namespace.conscrypt.visible = true
-# Keep in sync with ld.config.txt in the com.android.runtime APEX.
namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
namespace.conscrypt.asan.search.paths = /apex/com.android.conscrypt/${LIB}
namespace.conscrypt.links = runtime,default
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index 3f9882a..f4710d8 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -43,6 +43,9 @@
# can't be loaded in this namespace.
###############################################################################
namespace.default.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
+namespace.default.visible = true
namespace.default.search.paths = /system/${LIB}
namespace.default.search.paths += /%PRODUCT%/${LIB}
@@ -121,7 +124,7 @@
namespace.default.asan.permitted.paths += /apex/com.android.runtime/${LIB}/bionic
namespace.default.asan.permitted.paths += /system/${LIB}/bootstrap
-# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+# Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
# If a shared library or an executable requests a shared library that
# cannot be loaded into the default namespace, the dynamic linker tries
# to load the shared library from the runtime namespace. And then, if the
@@ -129,14 +132,12 @@
# dynamic linker tries to load the shared library from the resolv namespace.
# Finally, if all attempts fail, the dynamic linker returns an error.
namespace.default.links = runtime,resolv
-# Visible because some libraries are dlopen'ed, e.g. libopenjdk is dlopen'ed by
-# libart.
-namespace.default.visible = true
-namespace.default.link.runtime.shared_libs = libdexfile_external.so
+namespace.default.link.runtime.shared_libs = libandroidicu.so
+namespace.default.link.runtime.shared_libs += libdexfile_external.so
+namespace.default.link.runtime.shared_libs += libdexfiled_external.so
namespace.default.link.runtime.shared_libs += libnativebridge.so
namespace.default.link.runtime.shared_libs += libnativehelper.so
namespace.default.link.runtime.shared_libs += libnativeloader.so
-namespace.default.link.runtime.shared_libs += libandroidicu.so
# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
namespace.default.link.runtime.shared_libs += libpac.so
@@ -150,11 +151,13 @@
# "runtime" APEX namespace
#
# This namespace exposes externally accessible libraries from the Runtime APEX.
+# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
###############################################################################
namespace.runtime.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
namespace.runtime.visible = true
-# Keep in sync with ld.config.txt in the com.android.runtime APEX.
namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
namespace.runtime.links = default
@@ -187,11 +190,11 @@
# "conscrypt" APEX namespace
#
# This namespace is for libraries within the conscrypt APEX.
+# Keep in sync with the "conscrypt" namespace in art/build/apex/ld.config.txt.
###############################################################################
namespace.conscrypt.isolated = true
namespace.conscrypt.visible = true
-# Keep in sync with ld.config.txt in the com.android.runtime APEX.
namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
namespace.conscrypt.asan.search.paths = /apex/com.android.conscrypt/${LIB}
namespace.conscrypt.links = runtime,default
@@ -234,6 +237,8 @@
# Note that there is no link from the default namespace to this namespace.
###############################################################################
namespace.sphal.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
namespace.sphal.visible = true
namespace.sphal.search.paths = /odm/${LIB}
@@ -323,6 +328,8 @@
# This namespace is exclusively for vndk-sp libs.
###############################################################################
namespace.vndk.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
namespace.vndk.visible = true
namespace.vndk.search.paths = /odm/${LIB}/vndk-sp
@@ -430,10 +437,10 @@
# "runtime" APEX namespace
#
# This namespace exposes externally accessible libraries from the Runtime APEX.
+# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
###############################################################################
namespace.runtime.isolated = true
-# Keep in sync with ld.config.txt in the com.android.runtime APEX.
namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
namespace.runtime.links = system
@@ -504,6 +511,7 @@
namespace.system.links = runtime
namespace.system.link.runtime.shared_libs = libdexfile_external.so
+namespace.system.link.runtime.shared_libs += libdexfiled_external.so
namespace.system.link.runtime.shared_libs += libnativebridge.so
namespace.system.link.runtime.shared_libs += libnativehelper.so
namespace.system.link.runtime.shared_libs += libnativeloader.so
@@ -564,6 +572,10 @@
[unrestricted]
additional.namespaces = runtime,media,conscrypt,resolv
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
+namespace.default.visible = true
+
namespace.default.search.paths = /system/${LIB}
namespace.default.search.paths += /odm/${LIB}
namespace.default.search.paths += /vendor/${LIB}
@@ -575,15 +587,14 @@
namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
namespace.default.asan.search.paths += /vendor/${LIB}
-# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+# Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
namespace.default.links = runtime,resolv
-namespace.default.visible = true
-
-namespace.default.link.runtime.shared_libs = libdexfile_external.so
+namespace.default.link.runtime.shared_libs = libandroidicu.so
+namespace.default.link.runtime.shared_libs += libdexfile_external.so
+namespace.default.link.runtime.shared_libs += libdexfiled_external.so
namespace.default.link.runtime.shared_libs += libnativebridge.so
namespace.default.link.runtime.shared_libs += libnativehelper.so
namespace.default.link.runtime.shared_libs += libnativeloader.so
-namespace.default.link.runtime.shared_libs += libandroidicu.so
# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
namespace.default.link.runtime.shared_libs += libpac.so
@@ -594,11 +605,13 @@
# "runtime" APEX namespace
#
# This namespace exposes externally accessible libraries from the Runtime APEX.
+# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
###############################################################################
namespace.runtime.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
namespace.runtime.visible = true
-# Keep in sync with ld.config.txt in the com.android.runtime APEX.
namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
namespace.runtime.links = default
@@ -629,11 +642,11 @@
# "conscrypt" APEX namespace
#
# This namespace is for libraries within the conscrypt APEX.
+# Keep in sync with the "conscrypt" namespace in art/build/apex/ld.config.txt.
###############################################################################
namespace.conscrypt.isolated = true
namespace.conscrypt.visible = true
-# Keep in sync with ld.config.txt in the com.android.runtime APEX.
namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
namespace.conscrypt.asan.search.paths = /apex/com.android.conscrypt/${LIB}
namespace.conscrypt.links = runtime,default
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index 6d89886..4ce7f52 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -43,6 +43,9 @@
# partitions are also allowed temporarily.
###############################################################################
namespace.default.isolated = false
+# Visible because some libraries are dlopen'ed, e.g. libopenjdk is dlopen'ed by
+# libart.
+namespace.default.visible = true
namespace.default.search.paths = /system/${LIB}
namespace.default.search.paths += /odm/${LIB}
@@ -61,8 +64,7 @@
namespace.default.asan.search.paths += /data/asan/%PRODUCT_SERVICES%/${LIB}
namespace.default.asan.search.paths += /%PRODUCT_SERVICES%/${LIB}
-# Keep in sync with the platform namespace in the com.android.runtime APEX
-# ld.config.txt.
+# Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
# If a shared library or an executable requests a shared library that
# cannot be loaded into the default namespace, the dynamic linker tries
# to load the shared library from the runtime namespace. And then, if the
@@ -70,14 +72,12 @@
# dynamic linker tries to load the shared library from the resolv namespace.
# Finally, if all attempts fail, the dynamic linker returns an error.
namespace.default.links = runtime,resolv
-# Visible because some libraries are dlopen'ed, e.g. libopenjdk is dlopen'ed by
-# libart.
-namespace.default.visible = true
-namespace.default.link.runtime.shared_libs = libdexfile_external.so
+namespace.default.link.runtime.shared_libs = libandroidicu.so
+namespace.default.link.runtime.shared_libs += libdexfile_external.so
+namespace.default.link.runtime.shared_libs += libdexfiled_external.so
namespace.default.link.runtime.shared_libs += libnativebridge.so
namespace.default.link.runtime.shared_libs += libnativehelper.so
namespace.default.link.runtime.shared_libs += libnativeloader.so
-namespace.default.link.runtime.shared_libs += libandroidicu.so
# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
namespace.default.link.runtime.shared_libs += libpac.so
@@ -91,12 +91,13 @@
# "runtime" APEX namespace
#
# This namespace pulls in externally accessible libs from the Runtime APEX.
+# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
###############################################################################
namespace.runtime.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
namespace.runtime.visible = true
-# Keep in sync with the default namespace in the com.android.runtime APEX
-# ld.config.txt.
namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
namespace.runtime.links = default
@@ -129,11 +130,11 @@
# "conscrypt" APEX namespace
#
# This namespace is for libraries within the conscrypt APEX.
+# Keep in sync with the "conscrypt" namespace in art/build/apex/ld.config.txt.
###############################################################################
namespace.conscrypt.isolated = true
namespace.conscrypt.visible = true
-# Keep in sync with ld.config.txt in the com.android.runtime APEX.
namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
namespace.conscrypt.asan.search.paths = /apex/com.android.conscrypt/${LIB}
namespace.conscrypt.links = runtime,default
@@ -176,6 +177,8 @@
# Note that there is no link from the default namespace to this namespace.
###############################################################################
namespace.sphal.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
namespace.sphal.visible = true
namespace.sphal.search.paths = /odm/${LIB}
@@ -265,6 +268,8 @@
# This namespace is exclusively for vndk-sp libs.
###############################################################################
namespace.vndk.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
namespace.vndk.visible = true
namespace.vndk.search.paths = /odm/${LIB}/vndk-sp
@@ -357,6 +362,7 @@
namespace.default.links = runtime
namespace.default.link.runtime.shared_libs = libdexfile_external.so
+namespace.default.link.runtime.shared_libs += libdexfiled_external.so
namespace.default.link.runtime.shared_libs += libnativebridge.so
namespace.default.link.runtime.shared_libs += libnativehelper.so
namespace.default.link.runtime.shared_libs += libnativeloader.so
@@ -367,10 +373,10 @@
# "runtime" APEX namespace
#
# This namespace exposes externally accessible libraries from the Runtime APEX.
+# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
###############################################################################
namespace.runtime.isolated = true
-# Keep in sync with ld.config.txt in the com.android.runtime APEX.
namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
namespace.runtime.links = default
@@ -387,6 +393,10 @@
[unrestricted]
additional.namespaces = runtime,media,conscrypt,resolv
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
+namespace.default.visible = true
+
namespace.default.search.paths = /system/${LIB}
namespace.default.search.paths += /odm/${LIB}
namespace.default.search.paths += /vendor/${LIB}
@@ -398,15 +408,14 @@
namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
namespace.default.asan.search.paths += /vendor/${LIB}
-# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+# Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
namespace.default.links = runtime,resolv
-namespace.default.visible = true
-
-namespace.default.link.runtime.shared_libs = libdexfile_external.so
+namespace.default.link.runtime.shared_libs = libandroidicu.so
+namespace.default.link.runtime.shared_libs += libdexfile_external.so
+namespace.default.link.runtime.shared_libs += libdexfiled_external.so
namespace.default.link.runtime.shared_libs += libnativebridge.so
namespace.default.link.runtime.shared_libs += libnativehelper.so
namespace.default.link.runtime.shared_libs += libnativeloader.so
-namespace.default.link.runtime.shared_libs += libandroidicu.so
# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
namespace.default.link.runtime.shared_libs += libpac.so
@@ -417,11 +426,13 @@
# "runtime" APEX namespace
#
# This namespace exposes externally accessible libraries from the Runtime APEX.
+# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
###############################################################################
namespace.runtime.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
namespace.runtime.visible = true
-# Keep in sync with ld.config.txt in the com.android.runtime APEX.
namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
namespace.runtime.links = default
@@ -452,11 +463,11 @@
# "conscrypt" APEX namespace
#
# This namespace is for libraries within the conscrypt APEX.
+# Keep in sync with the "conscrypt" namespace in art/build/apex/ld.config.txt.
###############################################################################
namespace.conscrypt.isolated = true
namespace.conscrypt.visible = true
-# Keep in sync with ld.config.txt in the com.android.runtime APEX.
namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
namespace.conscrypt.asan.search.paths = /apex/com.android.conscrypt/${LIB}
namespace.conscrypt.links = runtime,default
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 295e704..84fa46e 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -80,7 +80,9 @@
chmod 0664 /dev/stune/top-app/tasks
chmod 0664 /dev/stune/rt/tasks
- # Create blkio tuning nodes
+ # Create blkio group and apply initial settings.
+ # This feature needs kernel to support it, and the
+ # device's init.rc must actually set the correct values.
mkdir /dev/blkio/background
chown system system /dev/blkio
chown system system /dev/blkio/background
@@ -88,6 +90,10 @@
chown system system /dev/blkio/background/tasks
chmod 0664 /dev/blkio/tasks
chmod 0664 /dev/blkio/background/tasks
+ write /dev/blkio/blkio.weight 1000
+ write /dev/blkio/background/blkio.weight 500
+ write /dev/blkio/blkio.group_idle 0
+ write /dev/blkio/background/blkio.group_idle 0
restorecon_recursive /mnt
@@ -405,6 +411,8 @@
class_start early_hal
on post-fs-data
+ mark_post_data
+
# Start checkpoint before we touch data
start vold
exec - system system -- /system/bin/vdc checkpoint prepareCheckpoint
@@ -747,9 +755,6 @@
on charger
class_start charger
-on property:vold.decrypt=trigger_reset_main
- class_reset main
-
on property:vold.decrypt=trigger_load_persist_props
load_persist_props
start logd
@@ -767,6 +772,8 @@
on property:vold.decrypt=trigger_restart_framework
# A/B update verifier that marks a successful boot.
exec_start update_verifier
+ class_start_post_data hal
+ class_start_post_data core
class_start main
class_start late_start
setprop service.bootanim.exit 0
@@ -775,6 +782,8 @@
on property:vold.decrypt=trigger_shutdown_framework
class_reset late_start
class_reset main
+ class_reset_post_data core
+ class_reset_post_data hal
on property:sys.boot_completed=1
bootchart stop