Merge "stats: Set min_sdk_version"
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index c8dbf77..dcf92be 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -3,3 +3,6 @@
[Builtin Hooks Options]
clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
+
+[Hook Scripts]
+aosp_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "."
diff --git a/adb/Android.bp b/adb/Android.bp
index 8779c0a..432770c 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -171,7 +171,6 @@
"sysdeps/errno.cpp",
"transport.cpp",
"transport_fd.cpp",
- "transport_local.cpp",
"types.cpp",
]
@@ -217,6 +216,7 @@
"client/adb_wifi.cpp",
"client/usb_libusb.cpp",
"client/usb_dispatch.cpp",
+ "client/transport_local.cpp",
"client/transport_mdns.cpp",
"client/transport_usb.cpp",
"client/pairing/pairing_client.cpp",
@@ -390,10 +390,11 @@
compile_multilib: "both",
srcs: libadb_srcs + libadb_linux_srcs + libadb_posix_srcs + [
+ "daemon/adb_wifi.cpp",
"daemon/auth.cpp",
"daemon/jdwp_service.cpp",
"daemon/logging.cpp",
- "daemon/adb_wifi.cpp",
+ "daemon/transport_local.cpp",
],
generated_headers: ["platform_tools_version"],
@@ -592,7 +593,6 @@
cc_binary {
name: "adbd",
defaults: ["adbd_defaults", "host_adbd_supported", "libadbd_binary_dependencies"],
- stl: "libc++_static",
recovery_available: true,
apex_available: ["com.android.adbd"],
diff --git a/adb/apex/Android.bp b/adb/apex/Android.bp
index 4346f67..ddb17da 100644
--- a/adb/apex/Android.bp
+++ b/adb/apex/Android.bp
@@ -1,6 +1,7 @@
apex_defaults {
name: "com.android.adbd-defaults",
updatable: true,
+ min_sdk_version: "R",
binaries: ["adbd"],
compile_multilib: "both",
diff --git a/adb/client/adb_install.cpp b/adb/client/adb_install.cpp
index 59c8563..e562f8b 100644
--- a/adb/client/adb_install.cpp
+++ b/adb/client/adb_install.cpp
@@ -154,6 +154,14 @@
*buf = '\0';
}
+static unique_fd send_command(const std::vector<std::string>& cmd_args, std::string* error) {
+ if (is_abb_exec_supported()) {
+ return send_abb_exec_command(cmd_args, error);
+ } else {
+ return unique_fd(adb_connect(android::base::Join(cmd_args, " "), error));
+ }
+}
+
static int install_app_streamed(int argc, const char** argv, bool use_fastdeploy) {
printf("Performing Streamed Install\n");
@@ -226,12 +234,7 @@
cmd_args.push_back("--apex");
}
- unique_fd remote_fd;
- if (use_abb_exec) {
- remote_fd = send_abb_exec_command(cmd_args, &error);
- } else {
- remote_fd.reset(adb_connect(android::base::Join(cmd_args, " "), &error));
- }
+ unique_fd remote_fd = send_command(cmd_args, &error);
if (remote_fd < 0) {
fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
return 1;
@@ -547,24 +550,28 @@
if (first_apk == -1) error_exit("need APK file on command line");
- std::string install_cmd;
- if (best_install_mode() == INSTALL_PUSH) {
- install_cmd = "exec:pm";
- } else {
- install_cmd = "exec:cmd package";
- }
+ const bool use_abb_exec = is_abb_exec_supported();
- std::string cmd = android::base::StringPrintf("%s install-create -S %" PRIu64,
- install_cmd.c_str(), total_size);
+ const std::string install_cmd =
+ use_abb_exec ? "package"
+ : best_install_mode() == INSTALL_PUSH ? "exec:pm" : "exec:cmd package";
+
+ std::vector<std::string> cmd_args = {install_cmd, "install-create", "-S",
+ std::to_string(total_size)};
+ cmd_args.reserve(first_apk + 4);
for (int i = 1; i < first_apk; i++) {
- cmd += " " + escape_arg(argv[i]);
+ if (use_abb_exec) {
+ cmd_args.push_back(argv[i]);
+ } else {
+ cmd_args.push_back(escape_arg(argv[i]));
+ }
}
// Create install session
std::string error;
char buf[BUFSIZ];
{
- unique_fd fd(adb_connect(cmd, &error));
+ unique_fd fd = send_command(cmd_args, &error);
if (fd < 0) {
fprintf(stderr, "adb: connect error for create: %s\n", error.c_str());
return EXIT_FAILURE;
@@ -586,6 +593,7 @@
fputs(buf, stderr);
return EXIT_FAILURE;
}
+ const auto session_id_str = std::to_string(session_id);
// Valid session, now stream the APKs
bool success = true;
@@ -598,10 +606,15 @@
goto finalize_session;
}
- std::string cmd =
- android::base::StringPrintf("%s install-write -S %" PRIu64 " %d %s -",
- install_cmd.c_str(), static_cast<uint64_t>(sb.st_size),
- session_id, android::base::Basename(file).c_str());
+ std::vector<std::string> cmd_args = {
+ install_cmd,
+ "install-write",
+ "-S",
+ std::to_string(sb.st_size),
+ session_id_str,
+ android::base::Basename(file),
+ "-",
+ };
unique_fd local_fd(adb_open(file, O_RDONLY | O_CLOEXEC));
if (local_fd < 0) {
@@ -611,7 +624,7 @@
}
std::string error;
- unique_fd remote_fd(adb_connect(cmd, &error));
+ unique_fd remote_fd = send_command(cmd_args, &error);
if (remote_fd < 0) {
fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
success = false;
@@ -636,10 +649,13 @@
finalize_session:
// Commit session if we streamed everything okay; otherwise abandon.
- std::string service = android::base::StringPrintf("%s install-%s %d", install_cmd.c_str(),
- success ? "commit" : "abandon", session_id);
+ std::vector<std::string> service_args = {
+ install_cmd,
+ success ? "install-commit" : "install-abandon",
+ session_id_str,
+ };
{
- unique_fd fd(adb_connect(service, &error));
+ unique_fd fd = send_command(service_args, &error);
if (fd < 0) {
fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str());
return EXIT_FAILURE;
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index d565e01..f0a287d 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -240,6 +240,7 @@
" $ANDROID_SERIAL serial number to connect to (see -s)\n"
" $ANDROID_LOG_TAGS tags to be used by logcat (see logcat --help)\n"
" $ADB_LOCAL_TRANSPORT_MAX_PORT max emulator scan port (default 5585, 16 emus)\n"
+ " $ADB_MDNS_AUTO_CONNECT comma-separated list of mdns services to allow auto-connect (default adb-tls-connect)\n"
);
// clang-format on
}
diff --git a/adb/transport_local.cpp b/adb/client/transport_local.cpp
similarity index 78%
rename from adb/transport_local.cpp
rename to adb/client/transport_local.cpp
index 5ec8e16..15a0724 100644
--- a/adb/transport_local.cpp
+++ b/adb/client/transport_local.cpp
@@ -38,10 +38,6 @@
#include <android-base/thread_annotations.h>
#include <cutils/sockets.h>
-#if !ADB_HOST
-#include <android-base/properties.h>
-#endif
-
#include "adb.h"
#include "adb_io.h"
#include "adb_unique_fd.h"
@@ -49,8 +45,6 @@
#include "socket_spec.h"
#include "sysdeps/chrono.h"
-#if ADB_HOST
-
// Android Wear has been using port 5601 in all of its documentation/tooling,
// but we search for emulators on ports [5554, 5555 + ADB_LOCAL_TRANSPORT_MAX].
// Avoid stomping on their port by restricting the active scanning range.
@@ -76,9 +70,8 @@
// We keep a map from emulator port to transport.
// TODO: weak_ptr?
-static auto& local_transports GUARDED_BY(local_transports_lock) =
- *new std::unordered_map<int, atransport*>();
-#endif /* ADB_HOST */
+static std::unordered_map<int, atransport*> local_transports
+ [[clang::no_destroy]] GUARDED_BY(local_transports_lock);
bool local_connect(int port) {
std::string dummy;
@@ -140,21 +133,19 @@
}
}
-
int local_connect_arbitrary_ports(int console_port, int adb_port, std::string* error) {
unique_fd fd;
-#if ADB_HOST
if (find_emulator_transport_by_adb_port(adb_port) != nullptr ||
find_emulator_transport_by_console_port(console_port) != nullptr) {
return -1;
}
- const char *host = getenv("ADBHOST");
+ const char* host = getenv("ADBHOST");
if (host) {
fd.reset(network_connect(host, adb_port, SOCK_STREAM, 0, error));
}
-#endif
+
if (fd < 0) {
fd.reset(network_loopback_client(adb_port, SOCK_STREAM, error));
}
@@ -173,8 +164,6 @@
return -1;
}
-#if ADB_HOST
-
static void PollAllLocalPortsForEmulator() {
// Try to connect to any number of running emulator instances.
for (int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT; port <= adb_local_transport_max_port;
@@ -194,8 +183,8 @@
// Retry emulators just kicked.
static std::vector<RetryPort>& retry_ports = *new std::vector<RetryPort>;
-std::mutex &retry_ports_lock = *new std::mutex;
-std::condition_variable &retry_ports_cond = *new std::condition_variable;
+std::mutex& retry_ports_lock = *new std::mutex;
+std::condition_variable& retry_ports_cond = *new std::condition_variable;
static void client_socket_thread(std::string_view) {
adb_thread_setname("client_socket_thread");
@@ -220,7 +209,7 @@
std::vector<RetryPort> next_ports;
for (auto& port : ports) {
VLOG(TRANSPORT) << "retry port " << port.port << ", last retry_count "
- << port.retry_count;
+ << port.retry_count;
if (local_connect(port.port)) {
VLOG(TRANSPORT) << "retry port " << port.port << " successfully";
continue;
@@ -240,77 +229,12 @@
}
}
-#else // !ADB_HOST
-
-void server_socket_thread(std::function<unique_fd(std::string_view, std::string*)> listen_func,
- std::string_view addr) {
- adb_thread_setname("server socket");
-
- unique_fd serverfd;
- std::string error;
-
- while (serverfd == -1) {
- errno = 0;
- serverfd = listen_func(addr, &error);
- if (errno == EAFNOSUPPORT || errno == EINVAL || errno == EPROTONOSUPPORT) {
- D("unrecoverable error: '%s'", error.c_str());
- return;
- } else if (serverfd < 0) {
- D("server: cannot bind socket yet: %s", error.c_str());
- std::this_thread::sleep_for(1s);
- continue;
- }
- close_on_exec(serverfd.get());
- }
-
- while (true) {
- D("server: trying to get new connection from fd %d", serverfd.get());
- unique_fd fd(adb_socket_accept(serverfd, nullptr, nullptr));
- if (fd >= 0) {
- D("server: new connection on fd %d", fd.get());
- close_on_exec(fd.get());
- disable_tcp_nagle(fd.get());
- std::string serial = android::base::StringPrintf("host-%d", fd.get());
- // We don't care about port value in "register_socket_transport" as it is used
- // only from ADB_HOST. "server_socket_thread" is never called from ADB_HOST.
- register_socket_transport(
- std::move(fd), std::move(serial), 0, 1,
- [](atransport*) { return ReconnectResult::Abort; }, false);
- }
- }
- D("transport: server_socket_thread() exiting");
-}
-
-#endif
-
-#if !ADB_HOST
-unique_fd adb_listen(std::string_view addr, std::string* error) {
- return unique_fd{socket_spec_listen(addr, error, nullptr)};
-}
-#endif
-
void local_init(const std::string& addr) {
-#if ADB_HOST
D("transport: local client init");
std::thread(client_socket_thread, addr).detach();
adb_local_transport_max_port_env_override();
-#elif !defined(__ANDROID__)
- // Host adbd.
- D("transport: local server init");
- std::thread(server_socket_thread, adb_listen, addr).detach();
-#else
- D("transport: local server init");
- // For the adbd daemon in the system image we need to distinguish
- // between the device, and the emulator.
- if (addr.starts_with("tcp:") && use_qemu_goldfish()) {
- std::thread(qemu_socket_thread, addr).detach();
- } else {
- std::thread(server_socket_thread, adb_listen, addr).detach();
- }
-#endif // !ADB_HOST
}
-#if ADB_HOST
struct EmulatorConnection : public FdConnection {
EmulatorConnection(unique_fd fd, int local_port)
: FdConnection(std::move(fd)), local_port_(local_port) {}
@@ -336,7 +260,7 @@
/* Only call this function if you already hold local_transports_lock. */
static atransport* find_emulator_transport_by_adb_port_locked(int adb_port)
- REQUIRES(local_transports_lock) {
+ REQUIRES(local_transports_lock) {
auto it = local_transports.find(adb_port);
if (it == local_transports.end()) {
return nullptr;
@@ -352,7 +276,6 @@
atransport* find_emulator_transport_by_console_port(int console_port) {
return find_transport(getEmulatorSerialString(console_port).c_str());
}
-#endif
std::string getEmulatorSerialString(int console_port) {
return android::base::StringPrintf("emulator-%d", console_port);
@@ -363,7 +286,6 @@
t->type = kTransportLocal;
-#if ADB_HOST
// Emulator connection.
if (local) {
auto emulator_connection = std::make_unique<EmulatorConnection>(std::move(fd), adb_port);
@@ -380,7 +302,6 @@
return fail;
}
-#endif
// Regular tcp connection.
auto fd_connection = std::make_unique<FdConnection>(std::move(fd));
diff --git a/adb/client/transport_mdns.cpp b/adb/client/transport_mdns.cpp
index 2bf062f..2b6aa7c 100644
--- a/adb/client/transport_mdns.cpp
+++ b/adb/client/transport_mdns.cpp
@@ -26,6 +26,7 @@
#include <memory>
#include <thread>
+#include <unordered_set>
#include <vector>
#include <android-base/stringprintf.h>
@@ -42,27 +43,75 @@
static DNSServiceRef service_refs[kNumADBDNSServices];
static fdevent* service_ref_fdes[kNumADBDNSServices];
+static auto& g_autoconn_whitelist = *new std::unordered_set<int>();
-static int adb_DNSServiceIndexByName(const char* regType) {
+static int adb_DNSServiceIndexByName(std::string_view regType) {
for (int i = 0; i < kNumADBDNSServices; ++i) {
- if (!strncmp(regType, kADBDNSServices[i], strlen(kADBDNSServices[i]))) {
+ if (!strncmp(regType.data(), kADBDNSServices[i], strlen(kADBDNSServices[i]))) {
return i;
}
}
return -1;
}
-static bool adb_DNSServiceShouldConnect(const char* regType, const char* serviceName) {
- int index = adb_DNSServiceIndexByName(regType);
- if (index == kADBTransportServiceRefIndex) {
- // Ignore adb-EMULATOR* service names, as it interferes with the
- // emulator ports that are already connected.
- if (android::base::StartsWith(serviceName, "adb-EMULATOR")) {
- LOG(INFO) << "Ignoring emulator transport service [" << serviceName << "]";
- return false;
+static void config_auto_connect_services() {
+ // ADB_MDNS_AUTO_CONNECT is a comma-delimited list of mdns services
+ // that are allowed to auto-connect. By default, only allow "adb-tls-connect"
+ // to auto-connect, since this is filtered down to auto-connect only to paired
+ // devices.
+ g_autoconn_whitelist.insert(kADBSecureConnectServiceRefIndex);
+ const char* srvs = getenv("ADB_MDNS_AUTO_CONNECT");
+ if (!srvs) {
+ return;
+ }
+
+ if (strcmp(srvs, "0") == 0) {
+ D("Disabling all auto-connecting");
+ g_autoconn_whitelist.clear();
+ return;
+ }
+
+ if (strcmp(srvs, "1") == 0) {
+ D("Allow all auto-connecting");
+ g_autoconn_whitelist.insert(kADBTransportServiceRefIndex);
+ return;
+ }
+
+ // Selectively choose which services to allow auto-connect.
+ // E.g. ADB_MDNS_AUTO_CONNECT=adb,adb-tls-connect would allow
+ // _adb._tcp and _adb-tls-connnect._tcp services to auto-connect.
+ auto srvs_list = android::base::Split(srvs, ",");
+ std::unordered_set<int> new_whitelist;
+ for (const auto& item : srvs_list) {
+ auto full_srv = android::base::StringPrintf("_%s._tcp", item.data());
+ int idx = adb_DNSServiceIndexByName(full_srv);
+ if (idx >= 0) {
+ new_whitelist.insert(idx);
}
}
- return (index == kADBTransportServiceRefIndex || index == kADBSecureConnectServiceRefIndex);
+
+ if (!new_whitelist.empty()) {
+ g_autoconn_whitelist = std::move(new_whitelist);
+ }
+}
+
+static bool adb_DNSServiceShouldAutoConnect(const char* regType, const char* serviceName) {
+ // Try to auto-connect to any "_adb" or "_adb-tls-connect" services excluding emulator services.
+ int index = adb_DNSServiceIndexByName(regType);
+ if (index != kADBTransportServiceRefIndex && index != kADBSecureConnectServiceRefIndex) {
+ return false;
+ }
+ if (g_autoconn_whitelist.find(index) == g_autoconn_whitelist.end()) {
+ D("Auto-connect for regType '%s' disabled", regType);
+ return false;
+ }
+ // Ignore adb-EMULATOR* service names, as it interferes with the
+ // emulator ports that are already connected.
+ if (android::base::StartsWith(serviceName, "adb-EMULATOR")) {
+ LOG(INFO) << "Ignoring emulator transport service [" << serviceName << "]";
+ return false;
+ }
+ return true;
}
// Use adb_DNSServiceRefSockFD() instead of calling DNSServiceRefSockFD()
@@ -196,7 +245,7 @@
// adb secure service needs to do something different from just
// connecting here.
- if (adb_DNSServiceShouldConnect(regType_.c_str(), serviceName_.c_str())) {
+ if (adb_DNSServiceShouldAutoConnect(regType_.c_str(), serviceName_.c_str())) {
std::string response;
D("Attempting to serviceName=[%s], regtype=[%s] ipaddr=(%s:%hu)", serviceName_.c_str(),
regType_.c_str(), ip_addr_, port_);
@@ -539,8 +588,15 @@
}
void init_mdns_transport_discovery_thread(void) {
- int errorCodes[kNumADBDNSServices];
+ config_auto_connect_services();
+ std::string res;
+ std::for_each(g_autoconn_whitelist.begin(), g_autoconn_whitelist.end(), [&](const int& i) {
+ res += kADBDNSServices[i];
+ res += ",";
+ });
+ D("mdns auto-connect whitelist: [%s]", res.data());
+ int errorCodes[kNumADBDNSServices];
for (int i = 0; i < kNumADBDNSServices; ++i) {
errorCodes[i] = DNSServiceBrowse(&service_refs[i], 0, 0, kADBDNSServices[i], nullptr,
on_service_browsed, nullptr);
diff --git a/adb/coverage/show.sh b/adb/coverage/show.sh
index 7ea7949..3b2faa3 100755
--- a/adb/coverage/show.sh
+++ b/adb/coverage/show.sh
@@ -5,8 +5,18 @@
OUTPUT_DIR=$(realpath $(dirname "$0"))
. "$OUTPUT_DIR"/include.sh
+BASE_PATH=/proc/self/cwd/system/core/adb
+PATHS=""
+if [[ $# == 0 ]]; then
+ PATHS=$BASE_PATH
+else
+ for arg in "$@"; do
+ PATHS="$PATHS $BASE_PATH/$arg"
+ done
+fi
+
cd $ANDROID_BUILD_TOP
llvm-cov show --instr-profile="$OUTPUT_DIR"/adbd.profdata \
$ANDROID_PRODUCT_OUT/apex/com.android.adbd/bin/adbd \
- /proc/self/cwd/system/core/adb \
+ $PATHS \
$ADB_TEST_BINARIES
diff --git a/adb/daemon/transport_local.cpp b/adb/daemon/transport_local.cpp
new file mode 100644
index 0000000..9e0b887
--- /dev/null
+++ b/adb/daemon/transport_local.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2007 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.
+ */
+
+#define TRACE_TAG TRANSPORT
+
+#include "sysdeps.h"
+#include "transport.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <condition_variable>
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <thread>
+#include <unordered_map>
+#include <vector>
+
+#include <android-base/parsenetaddress.h>
+#include <android-base/stringprintf.h>
+#include <android-base/thread_annotations.h>
+#include <cutils/sockets.h>
+
+#if !ADB_HOST
+#include <android-base/properties.h>
+#endif
+
+#include "adb.h"
+#include "adb_io.h"
+#include "adb_unique_fd.h"
+#include "adb_utils.h"
+#include "socket_spec.h"
+#include "sysdeps/chrono.h"
+
+void server_socket_thread(std::function<unique_fd(std::string_view, std::string*)> listen_func,
+ std::string_view addr) {
+ adb_thread_setname("server socket");
+
+ unique_fd serverfd;
+ std::string error;
+
+ while (serverfd == -1) {
+ errno = 0;
+ serverfd = listen_func(addr, &error);
+ if (errno == EAFNOSUPPORT || errno == EINVAL || errno == EPROTONOSUPPORT) {
+ D("unrecoverable error: '%s'", error.c_str());
+ return;
+ } else if (serverfd < 0) {
+ D("server: cannot bind socket yet: %s", error.c_str());
+ std::this_thread::sleep_for(1s);
+ continue;
+ }
+ close_on_exec(serverfd.get());
+ }
+
+ while (true) {
+ D("server: trying to get new connection from fd %d", serverfd.get());
+ unique_fd fd(adb_socket_accept(serverfd, nullptr, nullptr));
+ if (fd >= 0) {
+ D("server: new connection on fd %d", fd.get());
+ close_on_exec(fd.get());
+ disable_tcp_nagle(fd.get());
+ std::string serial = android::base::StringPrintf("host-%d", fd.get());
+ // We don't care about port value in "register_socket_transport" as it is used
+ // only from ADB_HOST. "server_socket_thread" is never called from ADB_HOST.
+ register_socket_transport(
+ std::move(fd), std::move(serial), 0, 1,
+ [](atransport*) { return ReconnectResult::Abort; }, false);
+ }
+ }
+ D("transport: server_socket_thread() exiting");
+}
+
+unique_fd adb_listen(std::string_view addr, std::string* error) {
+ return unique_fd{socket_spec_listen(addr, error, nullptr)};
+}
+
+void local_init(const std::string& addr) {
+#if !defined(__ANDROID__)
+ // Host adbd.
+ D("transport: local server init");
+ std::thread(server_socket_thread, adb_listen, addr).detach();
+#else
+ D("transport: local server init");
+ // For the adbd daemon in the system image we need to distinguish
+ // between the device, and the emulator.
+ if (addr.starts_with("tcp:") && use_qemu_goldfish()) {
+ std::thread(qemu_socket_thread, addr).detach();
+ } else {
+ std::thread(server_socket_thread, adb_listen, addr).detach();
+ }
+#endif // !ADB_HOST
+}
+
+int init_socket_transport(atransport* t, unique_fd fd, int adb_port, int local) {
+ t->type = kTransportLocal;
+ auto fd_connection = std::make_unique<FdConnection>(std::move(fd));
+ t->SetConnection(std::make_unique<BlockingConnectionAdapter>(std::move(fd_connection)));
+ return 0;
+}
diff --git a/base/Android.bp b/base/Android.bp
index 894ad6c..61fbc3d 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -51,6 +51,7 @@
"//apex_available:anyapex",
"//apex_available:platform",
],
+ min_sdk_version: "29",
}
cc_defaults {
@@ -132,6 +133,7 @@
"//apex_available:anyapex",
"//apex_available:platform",
],
+ min_sdk_version: "29",
}
cc_library_static {
@@ -157,6 +159,7 @@
"errors_test.cpp",
"expected_test.cpp",
"file_test.cpp",
+ "logging_splitters_test.cpp",
"logging_test.cpp",
"macros_test.cpp",
"mapped_file_test.cpp",
diff --git a/base/include/android-base/expected.h b/base/include/android-base/expected.h
index 9603bb1..9470344 100644
--- a/base/include/android-base/expected.h
+++ b/base/include/android-base/expected.h
@@ -182,7 +182,7 @@
!std::is_same_v<unexpected<E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
std::is_convertible_v<U&&, T> /* non-explicit */
)>
- // NOLINTNEXTLINE(google-explicit-constructor)
+ // NOLINTNEXTLINE(google-explicit-constructor,bugprone-forwarding-reference-overload)
constexpr expected(U&& v) : var_(std::in_place_index<0>, std::forward<U>(v)) {}
template <class U = T _ENABLE_IF(
@@ -192,6 +192,7 @@
!std::is_same_v<unexpected<E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
!std::is_convertible_v<U&&, T> /* explicit */
)>
+ // NOLINTNEXTLINE(bugprone-forwarding-reference-overload)
constexpr explicit expected(U&& v) : var_(std::in_place_index<0>, T(std::forward<U>(v))) {}
template<class G = E _ENABLE_IF(
@@ -387,13 +388,9 @@
template<class T1, class E1, class T2, class E2>
constexpr bool operator==(const expected<T1, E1>& x, const expected<T2, E2>& y) {
- if (x.has_value() != y.has_value()) {
- return false;
- } else if (!x.has_value()) {
- return x.error() == y.error();
- } else {
- return *x == *y;
- }
+ if (x.has_value() != y.has_value()) return false;
+ if (!x.has_value()) return x.error() == y.error();
+ return *x == *y;
}
template<class T1, class E1, class T2, class E2>
@@ -581,35 +578,23 @@
template<class E1, class E2>
constexpr bool operator==(const expected<void, E1>& x, const expected<void, E2>& y) {
- if (x.has_value() != y.has_value()) {
- return false;
- } else if (!x.has_value()) {
- return x.error() == y.error();
- } else {
- return true;
- }
+ if (x.has_value() != y.has_value()) return false;
+ if (!x.has_value()) return x.error() == y.error();
+ return true;
}
template<class T1, class E1, class E2>
constexpr bool operator==(const expected<T1, E1>& x, const expected<void, E2>& y) {
- if (x.has_value() != y.has_value()) {
- return false;
- } else if (!x.has_value()) {
- return x.error() == y.error();
- } else {
- return false;
- }
+ if (x.has_value() != y.has_value()) return false;
+ if (!x.has_value()) return x.error() == y.error();
+ return false;
}
template<class E1, class T2, class E2>
constexpr bool operator==(const expected<void, E1>& x, const expected<T2, E2>& y) {
- if (x.has_value() != y.has_value()) {
- return false;
- } else if (!x.has_value()) {
- return x.error() == y.error();
- } else {
- return false;
- }
+ if (x.has_value() != y.has_value()) return false;
+ if (!x.has_value()) return x.error() == y.error();
+ return false;
}
template<class E>
@@ -623,7 +608,7 @@
std::is_constructible_v<E, Err> &&
!std::is_same_v<std::remove_cv_t<std::remove_reference_t<E>>, std::in_place_t> &&
!std::is_same_v<std::remove_cv_t<std::remove_reference_t<E>>, unexpected>)>
- // NOLINTNEXTLINE(google-explicit-constructor)
+ // NOLINTNEXTLINE(google-explicit-constructor,bugprone-forwarding-reference-overload)
constexpr unexpected(Err&& e) : val_(std::forward<Err>(e)) {}
template<class U, class... Args _ENABLE_IF(
diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h
index accc225..26827fb 100644
--- a/base/include/android-base/logging.h
+++ b/base/include/android-base/logging.h
@@ -118,8 +118,10 @@
void SetDefaultTag(const std::string& tag);
-// We expose this even though it is the default because a user that wants to
-// override the default log buffer will have to construct this themselves.
+// The LogdLogger sends chunks of up to ~4000 bytes at a time to logd. It does not prevent other
+// threads from writing to logd between sending each chunk, so other threads may interleave their
+// messages. If preventing interleaving is required, then a custom logger that takes a lock before
+// calling this logger should be provided.
class LogdLogger {
public:
explicit LogdLogger(LogId default_log_id = android::base::MAIN);
diff --git a/base/include/android-base/result.h b/base/include/android-base/result.h
index 5e65876..56a4f3e 100644
--- a/base/include/android-base/result.h
+++ b/base/include/android-base/result.h
@@ -130,6 +130,7 @@
template <typename T>
Error& operator<<(T&& t) {
+ // NOLINTNEXTLINE(bugprone-suspicious-semicolon)
if constexpr (std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, ResultError>) {
errno_ = t.code();
return (*this) << t.message();
diff --git a/base/logging.cpp b/base/logging.cpp
index 3c73fea..6e9c67f 100644
--- a/base/logging.cpp
+++ b/base/logging.cpp
@@ -61,6 +61,7 @@
#include <android-base/threads.h>
#include "liblog_symbols.h"
+#include "logging_splitters.h"
namespace android {
namespace base {
@@ -190,11 +191,6 @@
}
}
-static std::mutex& LoggingLock() {
- static auto& logging_lock = *new std::mutex();
- return logging_lock;
-}
-
static LogFunction& Logger() {
#ifdef __ANDROID__
static auto& logger = *new LogFunction(LogdLogger());
@@ -239,8 +235,8 @@
static LogSeverity gMinimumLogSeverity = INFO;
#if defined(__linux__)
-void KernelLogger(android::base::LogId, android::base::LogSeverity severity,
- const char* tag, const char*, unsigned int, const char* msg) {
+static void KernelLogLine(const char* msg, int length, android::base::LogSeverity severity,
+ const char* tag) {
// clang-format off
static constexpr int kLogSeverityToKernelLogLevel[] = {
[android::base::VERBOSE] = 7, // KERN_DEBUG (there is no verbose kernel log
@@ -265,7 +261,7 @@
// TODO: should we automatically break up long lines into multiple lines?
// Or we could log but with something like "..." at the end?
char buf[1024];
- size_t size = snprintf(buf, sizeof(buf), "<%d>%s: %s\n", level, tag, msg);
+ size_t size = snprintf(buf, sizeof(buf), "<%d>%s: %.*s\n", level, tag, length, msg);
if (size > sizeof(buf)) {
size = snprintf(buf, sizeof(buf), "<%d>%s: %zu-byte message too long for printk\n",
level, tag, size);
@@ -276,6 +272,11 @@
iov[0].iov_len = size;
TEMP_FAILURE_RETRY(writev(klog_fd, iov, 1));
}
+
+void KernelLogger(android::base::LogId, android::base::LogSeverity severity, const char* tag,
+ const char*, unsigned int, const char* full_message) {
+ SplitByLines(full_message, KernelLogLine, severity, tag);
+}
#endif
void StderrLogger(LogId, LogSeverity severity, const char* tag, const char* file, unsigned int line,
@@ -288,21 +289,10 @@
#else
localtime_r(&t, &now);
#endif
+ auto output_string =
+ StderrOutputGenerator(now, getpid(), GetThreadId(), severity, tag, file, line, message);
- char timestamp[32];
- strftime(timestamp, sizeof(timestamp), "%m-%d %H:%M:%S", &now);
-
- static const char log_characters[] = "VDIWEFF";
- static_assert(arraysize(log_characters) - 1 == FATAL + 1,
- "Mismatch in size of log_characters and values in LogSeverity");
- char severity_char = log_characters[severity];
- if (file != nullptr) {
- fprintf(stderr, "%s %c %s %5d %5" PRIu64 " %s:%u] %s\n", tag ? tag : "nullptr", severity_char,
- timestamp, getpid(), GetThreadId(), file, line, message);
- } else {
- fprintf(stderr, "%s %c %s %5d %5" PRIu64 " %s\n", tag ? tag : "nullptr", severity_char,
- timestamp, getpid(), GetThreadId(), message);
- }
+ fputs(output_string.c_str(), stderr);
}
void StdioLogger(LogId, LogSeverity severity, const char* /*tag*/, const char* /*file*/,
@@ -324,26 +314,9 @@
abort();
}
-
-LogdLogger::LogdLogger(LogId default_log_id) : default_log_id_(default_log_id) {
-}
-
-void LogdLogger::operator()(LogId id, LogSeverity severity, const char* tag,
- const char* file, unsigned int line,
- const char* message) {
- int32_t priority = LogSeverityToPriority(severity);
- if (id == DEFAULT) {
- id = default_log_id_;
- }
-
+static void LogdLogChunk(LogId id, LogSeverity severity, const char* tag, const char* message) {
int32_t lg_id = LogIdTolog_id_t(id);
-
- char log_message_with_file[4068]; // LOGGER_ENTRY_MAX_PAYLOAD, not available in the NDK.
- if (priority == ANDROID_LOG_FATAL && file != nullptr) {
- snprintf(log_message_with_file, sizeof(log_message_with_file), "%s:%u] %s", file, line,
- message);
- message = log_message_with_file;
- }
+ int32_t priority = LogSeverityToPriority(severity);
static auto& liblog_functions = GetLibLogFunctions();
if (liblog_functions) {
@@ -355,6 +328,17 @@
}
}
+LogdLogger::LogdLogger(LogId default_log_id) : default_log_id_(default_log_id) {}
+
+void LogdLogger::operator()(LogId id, LogSeverity severity, const char* tag, const char* file,
+ unsigned int line, const char* message) {
+ if (id == DEFAULT) {
+ id = default_log_id_;
+ }
+
+ SplitByLogdChunks(id, severity, tag, file, line, message, LogdLogChunk);
+}
+
void InitLogging(char* argv[], LogFunction&& logger, AbortFunction&& aborter) {
SetLogger(std::forward<LogFunction>(logger));
SetAborter(std::forward<AbortFunction>(aborter));
@@ -515,26 +499,8 @@
#endif
}
- {
- // Do the actual logging with the lock held.
- std::lock_guard<std::mutex> lock(LoggingLock());
- if (msg.find('\n') == std::string::npos) {
- LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), data_->GetTag(),
- msg.c_str());
- } else {
- msg += '\n';
- size_t i = 0;
- while (i < msg.size()) {
- size_t nl = msg.find('\n', i);
- msg[nl] = '\0';
- LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), data_->GetTag(),
- &msg[i]);
- // Undo the zero-termination so we can give the complete message to the aborter.
- msg[nl] = '\n';
- i = nl + 1;
- }
- }
- }
+ LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), data_->GetTag(),
+ msg.c_str());
// Abort if necessary.
if (data_->GetSeverity() == FATAL) {
diff --git a/base/logging_splitters.h b/base/logging_splitters.h
new file mode 100644
index 0000000..2ec2b20
--- /dev/null
+++ b/base/logging_splitters.h
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <inttypes.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+#define LOGGER_ENTRY_MAX_PAYLOAD 4068 // This constant is not in the NDK.
+
+namespace android {
+namespace base {
+
+// This splits the message up line by line, by calling log_function with a pointer to the start of
+// each line and the size up to the newline character. It sends size = -1 for the final line.
+template <typename F, typename... Args>
+static void SplitByLines(const char* msg, const F& log_function, Args&&... args) {
+ const char* newline = strchr(msg, '\n');
+ while (newline != nullptr) {
+ log_function(msg, newline - msg, args...);
+ msg = newline + 1;
+ newline = strchr(msg, '\n');
+ }
+
+ log_function(msg, -1, args...);
+}
+
+// This splits the message up into chunks that logs can process delimited by new lines. It calls
+// log_function with the exact null terminated message that should be sent to logd.
+// Note, despite the loops and snprintf's, if severity is not fatal and there are no new lines,
+// this function simply calls log_function with msg without any extra overhead.
+template <typename F>
+static void SplitByLogdChunks(LogId log_id, LogSeverity severity, const char* tag, const char* file,
+ unsigned int line, const char* msg, const F& log_function) {
+ // The maximum size of a payload, after the log header that logd will accept is
+ // LOGGER_ENTRY_MAX_PAYLOAD, so subtract the other elements in the payload to find the size of
+ // the string that we can log in each pass.
+ // The protocol is documented in liblog/README.protocol.md.
+ // Specifically we subtract a byte for the priority, the length of the tag + its null terminator,
+ // and an additional byte for the null terminator on the payload. We subtract an additional 32
+ // bytes for slack, similar to java/android/util/Log.java.
+ ptrdiff_t max_size = LOGGER_ENTRY_MAX_PAYLOAD - strlen(tag) - 35;
+ if (max_size <= 0) {
+ abort();
+ }
+ // If we're logging a fatal message, we'll append the file and line numbers.
+ bool add_file = file != nullptr && (severity == FATAL || severity == FATAL_WITHOUT_ABORT);
+
+ std::string file_header;
+ if (add_file) {
+ file_header = StringPrintf("%s:%u] ", file, line);
+ }
+ int file_header_size = file_header.size();
+
+ __attribute__((uninitialized)) char logd_chunk[max_size + 1];
+ ptrdiff_t chunk_position = 0;
+
+ auto call_log_function = [&]() {
+ log_function(log_id, severity, tag, logd_chunk);
+ chunk_position = 0;
+ };
+
+ auto write_to_logd_chunk = [&](const char* message, int length) {
+ int size_written = 0;
+ const char* new_line = chunk_position > 0 ? "\n" : "";
+ if (add_file) {
+ size_written = snprintf(logd_chunk + chunk_position, sizeof(logd_chunk) - chunk_position,
+ "%s%s%.*s", new_line, file_header.c_str(), length, message);
+ } else {
+ size_written = snprintf(logd_chunk + chunk_position, sizeof(logd_chunk) - chunk_position,
+ "%s%.*s", new_line, length, message);
+ }
+
+ // This should never fail, if it does and we set size_written to 0, which will skip this line
+ // and move to the next one.
+ if (size_written < 0) {
+ size_written = 0;
+ }
+ chunk_position += size_written;
+ };
+
+ const char* newline = strchr(msg, '\n');
+ while (newline != nullptr) {
+ // If we have data in the buffer and this next line doesn't fit, write the buffer.
+ if (chunk_position != 0 && chunk_position + (newline - msg) + 1 + file_header_size > max_size) {
+ call_log_function();
+ }
+
+ // Otherwise, either the next line fits or we have any empty buffer and too large of a line to
+ // ever fit, in both cases, we add it to the buffer and continue.
+ write_to_logd_chunk(msg, newline - msg);
+
+ msg = newline + 1;
+ newline = strchr(msg, '\n');
+ }
+
+ // If we have left over data in the buffer and we can fit the rest of msg, add it to the buffer
+ // then write the buffer.
+ if (chunk_position != 0 &&
+ chunk_position + static_cast<int>(strlen(msg)) + 1 + file_header_size <= max_size) {
+ write_to_logd_chunk(msg, -1);
+ call_log_function();
+ } else {
+ // If the buffer is not empty and we can't fit the rest of msg into it, write its contents.
+ if (chunk_position != 0) {
+ call_log_function();
+ }
+ // Then write the rest of the msg.
+ if (add_file) {
+ snprintf(logd_chunk, sizeof(logd_chunk), "%s%s", file_header.c_str(), msg);
+ log_function(log_id, severity, tag, logd_chunk);
+ } else {
+ log_function(log_id, severity, tag, msg);
+ }
+ }
+}
+
+static std::pair<int, int> CountSizeAndNewLines(const char* message) {
+ int size = 0;
+ int new_lines = 0;
+ while (*message != '\0') {
+ size++;
+ if (*message == '\n') {
+ ++new_lines;
+ }
+ ++message;
+ }
+ return {size, new_lines};
+}
+
+// This adds the log header to each line of message and returns it as a string intended to be
+// written to stderr.
+static std::string StderrOutputGenerator(const struct tm& now, int pid, uint64_t tid,
+ LogSeverity severity, const char* tag, const char* file,
+ unsigned int line, const char* message) {
+ char timestamp[32];
+ strftime(timestamp, sizeof(timestamp), "%m-%d %H:%M:%S", &now);
+
+ static const char log_characters[] = "VDIWEFF";
+ static_assert(arraysize(log_characters) - 1 == FATAL + 1,
+ "Mismatch in size of log_characters and values in LogSeverity");
+ char severity_char = log_characters[severity];
+ std::string line_prefix;
+ if (file != nullptr) {
+ line_prefix = StringPrintf("%s %c %s %5d %5" PRIu64 " %s:%u] ", tag ? tag : "nullptr",
+ severity_char, timestamp, pid, tid, file, line);
+ } else {
+ line_prefix = StringPrintf("%s %c %s %5d %5" PRIu64 " ", tag ? tag : "nullptr", severity_char,
+ timestamp, pid, tid);
+ }
+
+ auto [size, new_lines] = CountSizeAndNewLines(message);
+ std::string output_string;
+ output_string.reserve(size + new_lines * line_prefix.size() + 1);
+
+ auto concat_lines = [&](const char* message, int size) {
+ output_string.append(line_prefix);
+ if (size == -1) {
+ output_string.append(message);
+ } else {
+ output_string.append(message, size);
+ }
+ output_string.append("\n");
+ };
+ SplitByLines(message, concat_lines);
+ return output_string;
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/logging_splitters_test.cpp b/base/logging_splitters_test.cpp
new file mode 100644
index 0000000..679d19e
--- /dev/null
+++ b/base/logging_splitters_test.cpp
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "logging_splitters.h"
+
+#include <string>
+#include <vector>
+
+#include <android-base/strings.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace base {
+
+void TestNewlineSplitter(const std::string& input,
+ const std::vector<std::string>& expected_output) {
+ std::vector<std::string> output;
+ auto logger_function = [&](const char* msg, int length) {
+ if (length == -1) {
+ output.push_back(msg);
+ } else {
+ output.push_back(std::string(msg, length));
+ }
+ };
+ SplitByLines(input.c_str(), logger_function);
+
+ EXPECT_EQ(expected_output, output);
+}
+
+TEST(logging_splitters, NewlineSplitter_EmptyString) {
+ TestNewlineSplitter("", std::vector<std::string>{""});
+}
+
+TEST(logging_splitters, NewlineSplitter_BasicString) {
+ TestNewlineSplitter("normal string", std::vector<std::string>{"normal string"});
+}
+
+TEST(logging_splitters, NewlineSplitter_ormalBasicStringTrailingNewline) {
+ TestNewlineSplitter("normal string\n", std::vector<std::string>{"normal string", ""});
+}
+
+TEST(logging_splitters, NewlineSplitter_MultilineTrailing) {
+ TestNewlineSplitter("normal string\nsecond string\nthirdstring",
+ std::vector<std::string>{"normal string", "second string", "thirdstring"});
+}
+
+TEST(logging_splitters, NewlineSplitter_MultilineTrailingNewline) {
+ TestNewlineSplitter(
+ "normal string\nsecond string\nthirdstring\n",
+ std::vector<std::string>{"normal string", "second string", "thirdstring", ""});
+}
+
+TEST(logging_splitters, NewlineSplitter_MultilineEmbeddedNewlines) {
+ TestNewlineSplitter(
+ "normal string\n\n\nsecond string\n\nthirdstring\n",
+ std::vector<std::string>{"normal string", "", "", "second string", "", "thirdstring", ""});
+}
+
+void TestLogdChunkSplitter(const std::string& tag, const std::string& file,
+ const std::string& input,
+ const std::vector<std::string>& expected_output) {
+ std::vector<std::string> output;
+ auto logger_function = [&](LogId, LogSeverity, const char*, const char* msg) {
+ output.push_back(msg);
+ };
+
+ SplitByLogdChunks(MAIN, FATAL, tag.c_str(), file.empty() ? nullptr : file.c_str(), 1000,
+ input.c_str(), logger_function);
+
+ auto return_lengths = [&] {
+ std::string sizes;
+ sizes += "expected_output sizes:";
+ for (const auto& string : expected_output) {
+ sizes += " " + std::to_string(string.size());
+ }
+ sizes += "\noutput sizes:";
+ for (const auto& string : output) {
+ sizes += " " + std::to_string(string.size());
+ }
+ return sizes;
+ };
+
+ EXPECT_EQ(expected_output, output) << return_lengths();
+}
+
+TEST(logging_splitters, LogdChunkSplitter_EmptyString) {
+ TestLogdChunkSplitter("tag", "", "", std::vector<std::string>{""});
+}
+
+TEST(logging_splitters, LogdChunkSplitter_BasicString) {
+ TestLogdChunkSplitter("tag", "", "normal string", std::vector<std::string>{"normal string"});
+}
+
+TEST(logging_splitters, LogdChunkSplitter_NormalBasicStringTrailingNewline) {
+ TestLogdChunkSplitter("tag", "", "normal string\n", std::vector<std::string>{"normal string\n"});
+}
+
+TEST(logging_splitters, LogdChunkSplitter_MultilineTrailing) {
+ TestLogdChunkSplitter("tag", "", "normal string\nsecond string\nthirdstring",
+ std::vector<std::string>{"normal string\nsecond string\nthirdstring"});
+}
+
+TEST(logging_splitters, LogdChunkSplitter_MultilineTrailingNewline) {
+ TestLogdChunkSplitter("tag", "", "normal string\nsecond string\nthirdstring\n",
+ std::vector<std::string>{"normal string\nsecond string\nthirdstring\n"});
+}
+
+TEST(logging_splitters, LogdChunkSplitter_MultilineEmbeddedNewlines) {
+ TestLogdChunkSplitter(
+ "tag", "", "normal string\n\n\nsecond string\n\nthirdstring\n",
+ std::vector<std::string>{"normal string\n\n\nsecond string\n\nthirdstring\n"});
+}
+
+// This test should return the same string, the logd logger itself will truncate down to size.
+// This has historically been the behavior both in libbase and liblog.
+TEST(logging_splitters, LogdChunkSplitter_HugeLineNoNewline) {
+ auto long_string = std::string(LOGGER_ENTRY_MAX_PAYLOAD, 'x');
+ ASSERT_EQ(LOGGER_ENTRY_MAX_PAYLOAD, static_cast<int>(long_string.size()));
+
+ TestLogdChunkSplitter("tag", "", long_string, std::vector{long_string});
+}
+
+std::string ReduceToMaxSize(const std::string& tag, const std::string& string) {
+ return string.substr(0, LOGGER_ENTRY_MAX_PAYLOAD - tag.size() - 35);
+}
+
+TEST(logging_splitters, LogdChunkSplitter_MultipleHugeLineNoNewline) {
+ auto long_string_x = std::string(LOGGER_ENTRY_MAX_PAYLOAD, 'x');
+ auto long_string_y = std::string(LOGGER_ENTRY_MAX_PAYLOAD, 'y');
+ auto long_string_z = std::string(LOGGER_ENTRY_MAX_PAYLOAD, 'z');
+
+ auto long_strings = long_string_x + '\n' + long_string_y + '\n' + long_string_z;
+
+ std::string tag = "tag";
+ std::vector expected = {ReduceToMaxSize(tag, long_string_x), ReduceToMaxSize(tag, long_string_y),
+ long_string_z};
+
+ TestLogdChunkSplitter(tag, "", long_strings, expected);
+}
+
+// With a ~4k buffer, we should print 2 long strings per logger call.
+TEST(logging_splitters, LogdChunkSplitter_Multiple2kLines) {
+ std::vector expected = {
+ std::string(2000, 'a') + '\n' + std::string(2000, 'b'),
+ std::string(2000, 'c') + '\n' + std::string(2000, 'd'),
+ std::string(2000, 'e') + '\n' + std::string(2000, 'f'),
+ };
+
+ auto long_strings = Join(expected, '\n');
+
+ TestLogdChunkSplitter("tag", "", long_strings, expected);
+}
+
+TEST(logging_splitters, LogdChunkSplitter_ExactSizedLines) {
+ const char* tag = "tag";
+ ptrdiff_t max_size = LOGGER_ENTRY_MAX_PAYLOAD - strlen(tag) - 35;
+ auto long_string_a = std::string(max_size, 'a');
+ auto long_string_b = std::string(max_size, 'b');
+ auto long_string_c = std::string(max_size, 'c');
+
+ auto long_strings = long_string_a + '\n' + long_string_b + '\n' + long_string_c;
+
+ TestLogdChunkSplitter(tag, "", long_strings,
+ std::vector{long_string_a, long_string_b, long_string_c});
+}
+
+TEST(logging_splitters, LogdChunkSplitter_UnderEqualOver) {
+ std::string tag = "tag";
+ ptrdiff_t max_size = LOGGER_ENTRY_MAX_PAYLOAD - tag.size() - 35;
+
+ auto first_string_size = 1000;
+ auto first_string = std::string(first_string_size, 'a');
+ auto second_string_size = max_size - first_string_size - 1;
+ auto second_string = std::string(second_string_size, 'b');
+
+ auto exact_string = std::string(max_size, 'c');
+
+ auto large_string = std::string(max_size + 50, 'd');
+
+ auto final_string = std::string("final string!\n\nfinal \n \n final \n");
+
+ std::vector expected = {first_string + '\n' + second_string, exact_string,
+ ReduceToMaxSize(tag, large_string), final_string};
+
+ std::vector input_strings = {first_string + '\n' + second_string, exact_string, large_string,
+ final_string};
+ auto long_strings = Join(input_strings, '\n');
+
+ TestLogdChunkSplitter(tag, "", long_strings, expected);
+}
+
+TEST(logging_splitters, LogdChunkSplitter_WithFile) {
+ std::string tag = "tag";
+ std::string file = "/path/to/myfile.cpp";
+ int line = 1000;
+ auto file_header = StringPrintf("%s:%d] ", file.c_str(), line);
+ ptrdiff_t max_size = LOGGER_ENTRY_MAX_PAYLOAD - tag.size() - 35;
+
+ auto first_string_size = 1000;
+ auto first_string = std::string(first_string_size, 'a');
+ auto second_string_size = max_size - first_string_size - 1 - 2 * file_header.size();
+ auto second_string = std::string(second_string_size, 'b');
+
+ auto exact_string = std::string(max_size - file_header.size(), 'c');
+
+ auto large_string = std::string(max_size + 50, 'd');
+
+ auto final_string = std::string("final string!");
+
+ std::vector expected = {
+ file_header + first_string + '\n' + file_header + second_string, file_header + exact_string,
+ file_header + ReduceToMaxSize(file_header + tag, large_string), file_header + final_string};
+
+ std::vector input_strings = {first_string + '\n' + second_string, exact_string, large_string,
+ final_string};
+ auto long_strings = Join(input_strings, '\n');
+
+ TestLogdChunkSplitter(tag, file, long_strings, expected);
+}
+
+// We set max_size based off of tag, so if it's too large, the buffer will be sized wrong.
+// We could recover from this, but it's certainly an error for someone to attempt to use a tag this
+// large, so we abort instead.
+TEST(logging_splitters, LogdChunkSplitter_TooLongTag) {
+ auto long_tag = std::string(5000, 'x');
+ auto logger_function = [](LogId, LogSeverity, const char*, const char*) {};
+ ASSERT_DEATH(
+ SplitByLogdChunks(MAIN, ERROR, long_tag.c_str(), nullptr, 0, "message", logger_function), "");
+}
+
+// We do handle excessively large file names correctly however.
+TEST(logging_splitters, LogdChunkSplitter_TooLongFile) {
+ auto long_file = std::string(5000, 'x');
+ std::string tag = "tag";
+
+ std::vector expected = {ReduceToMaxSize(tag, long_file), ReduceToMaxSize(tag, long_file)};
+
+ TestLogdChunkSplitter(tag, long_file, "can't see me\nor me", expected);
+}
+
+void TestStderrOutputGenerator(const char* tag, const char* file, int line, const char* message,
+ const std::string& expected) {
+ // All log messages will show "01-01 00:00:00"
+ struct tm now = {
+ .tm_sec = 0,
+ .tm_min = 0,
+ .tm_hour = 0,
+ .tm_mday = 1,
+ .tm_mon = 0,
+ .tm_year = 1970,
+ };
+
+ int pid = 1234; // All log messages will have 1234 for their PID.
+ uint64_t tid = 4321; // All log messages will have 4321 for their TID.
+
+ auto result = StderrOutputGenerator(now, pid, tid, ERROR, tag, file, line, message);
+ EXPECT_EQ(expected, result);
+}
+
+TEST(logging_splitters, StderrOutputGenerator_Basic) {
+ TestStderrOutputGenerator(nullptr, nullptr, 0, "simple message",
+ "nullptr E 01-01 00:00:00 1234 4321 simple message\n");
+ TestStderrOutputGenerator("tag", nullptr, 0, "simple message",
+ "tag E 01-01 00:00:00 1234 4321 simple message\n");
+ TestStderrOutputGenerator(
+ "tag", "/path/to/some/file", 0, "simple message",
+ "tag E 01-01 00:00:00 1234 4321 /path/to/some/file:0] simple message\n");
+}
+
+TEST(logging_splitters, StderrOutputGenerator_NewlineTagAndFile) {
+ TestStderrOutputGenerator("tag\n\n", nullptr, 0, "simple message",
+ "tag\n\n E 01-01 00:00:00 1234 4321 simple message\n");
+ TestStderrOutputGenerator(
+ "tag", "/path/to/some/file\n\n", 0, "simple message",
+ "tag E 01-01 00:00:00 1234 4321 /path/to/some/file\n\n:0] simple message\n");
+}
+
+TEST(logging_splitters, StderrOutputGenerator_TrailingNewLine) {
+ TestStderrOutputGenerator(
+ "tag", nullptr, 0, "simple message\n",
+ "tag E 01-01 00:00:00 1234 4321 simple message\ntag E 01-01 00:00:00 1234 4321 \n");
+}
+
+TEST(logging_splitters, StderrOutputGenerator_MultiLine) {
+ const char* expected_result =
+ "tag E 01-01 00:00:00 1234 4321 simple message\n"
+ "tag E 01-01 00:00:00 1234 4321 \n"
+ "tag E 01-01 00:00:00 1234 4321 \n"
+ "tag E 01-01 00:00:00 1234 4321 another message \n"
+ "tag E 01-01 00:00:00 1234 4321 \n"
+ "tag E 01-01 00:00:00 1234 4321 final message \n"
+ "tag E 01-01 00:00:00 1234 4321 \n"
+ "tag E 01-01 00:00:00 1234 4321 \n"
+ "tag E 01-01 00:00:00 1234 4321 \n";
+
+ TestStderrOutputGenerator("tag", nullptr, 0,
+ "simple message\n\n\nanother message \n\n final message \n\n\n",
+ expected_result);
+}
+
+TEST(logging_splitters, StderrOutputGenerator_MultiLineLong) {
+ auto long_string_a = std::string(4000, 'a');
+ auto long_string_b = std::string(4000, 'b');
+
+ auto message = long_string_a + '\n' + long_string_b;
+ auto expected_result = "tag E 01-01 00:00:00 1234 4321 " + long_string_a + '\n' +
+ "tag E 01-01 00:00:00 1234 4321 " + long_string_b + '\n';
+ TestStderrOutputGenerator("tag", nullptr, 0, message.c_str(), expected_result);
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/logging_test.cpp b/base/logging_test.cpp
index 3a453e6..593e2c1 100644
--- a/base/logging_test.cpp
+++ b/base/logging_test.cpp
@@ -24,8 +24,10 @@
#include <regex>
#include <string>
+#include <thread>
#include "android-base/file.h"
+#include "android-base/scopeguard.h"
#include "android-base/stringprintf.h"
#include "android-base/test_utils.h"
@@ -596,7 +598,7 @@
CapturedStderr cap;
LOG(FATAL) << "foo\nbar";
- EXPECT_EQ(CountLineAborter::newline_count, 1U + 1U); // +1 for final '\n'.
+ EXPECT_EQ(CountLineAborter::newline_count, 1U);
}
__attribute__((constructor)) void TestLoggingInConstructor() {
@@ -617,3 +619,55 @@
// Whereas ERROR logging includes the program name.
ASSERT_EQ(android::base::Basename(android::base::GetExecutablePath()) + ": err\n", cap_err.str());
}
+
+TEST(logging, ForkSafe) {
+#if !defined(_WIN32)
+ using namespace android::base;
+ SetLogger(
+ [&](LogId, LogSeverity, const char*, const char*, unsigned int, const char*) { sleep(3); });
+
+ auto guard = make_scope_guard([&] {
+#ifdef __ANDROID__
+ SetLogger(LogdLogger());
+#else
+ SetLogger(StderrLogger);
+#endif
+ });
+
+ auto thread = std::thread([] {
+ LOG(ERROR) << "This should sleep for 3 seconds, long enough to fork another process, if there "
+ "is no intervention";
+ });
+ thread.detach();
+
+ auto pid = fork();
+ ASSERT_NE(-1, pid);
+
+ if (pid == 0) {
+ // Reset the logger, so the next message doesn't sleep().
+ SetLogger([](LogId, LogSeverity, const char*, const char*, unsigned int, const char*) {});
+ LOG(ERROR) << "This should succeed in the child, only if libbase is forksafe.";
+ _exit(EXIT_SUCCESS);
+ }
+
+ // Wait for up to 3 seconds for the child to exit.
+ int tries = 3;
+ bool found_child = false;
+ while (tries-- > 0) {
+ auto result = waitpid(pid, nullptr, WNOHANG);
+ EXPECT_NE(-1, result);
+ if (result == pid) {
+ found_child = true;
+ break;
+ }
+ sleep(1);
+ }
+
+ EXPECT_TRUE(found_child);
+
+ // Kill the child if it did not exit.
+ if (!found_child) {
+ kill(pid, SIGKILL);
+ }
+#endif
+}
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index e0168d5..d6b2e25 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -43,6 +43,7 @@
#include <android-base/unique_fd.h>
#include <android/log.h>
#include <log/log.h>
+#include <log/log_read.h>
#include <log/logprint.h>
#include <private/android_filesystem_config.h>
#include <unwindstack/DexFiles.h>
diff --git a/fs_mgr/README.overlayfs.md b/fs_mgr/README.overlayfs.md
index f579078..ca782b9 100644
--- a/fs_mgr/README.overlayfs.md
+++ b/fs_mgr/README.overlayfs.md
@@ -42,7 +42,7 @@
$ adb push <source> <destination>
$ adb reboot
-Note that you can replace these two lines:
+Note that you can replace these two lines in the above sequence:
$ adb disable-verity
$ adb reboot
@@ -51,7 +51,7 @@
$ adb remount -R
-**Note:** _adb reboot -R_ won’t reboot if the device is already in the adb remount state.
+**Note:** _adb remount -R_ won’t reboot if the device is already in the adb remount state.
None of this changes if OverlayFS needs to be engaged.
The decisions whether to use traditional direct file-system remount,
diff --git a/fs_mgr/libfiemap/Android.bp b/fs_mgr/libfiemap/Android.bp
index 9d18a44..bde9d0a 100644
--- a/fs_mgr/libfiemap/Android.bp
+++ b/fs_mgr/libfiemap/Android.bp
@@ -69,6 +69,7 @@
"libdm",
"libfs_mgr",
"liblog",
+ "libgsi",
],
data: [
diff --git a/fs_mgr/libfiemap/fiemap_writer_test.cpp b/fs_mgr/libfiemap/fiemap_writer_test.cpp
index 22a3722..3c8ab42 100644
--- a/fs_mgr/libfiemap/fiemap_writer_test.cpp
+++ b/fs_mgr/libfiemap/fiemap_writer_test.cpp
@@ -35,6 +35,7 @@
#include <libdm/loop_control.h>
#include <libfiemap/fiemap_writer.h>
#include <libfiemap/split_fiemap_writer.h>
+#include <libgsi/libgsi.h>
#include "utility.h"
@@ -148,7 +149,10 @@
FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize);
EXPECT_EQ(fptr->size(), gBlockSize);
EXPECT_EQ(fptr->bdev_path().find("/dev/block/"), size_t(0));
- EXPECT_EQ(fptr->bdev_path().find("/dev/block/dm-"), string::npos);
+
+ if (!android::gsi::IsGsiRunning()) {
+ EXPECT_EQ(fptr->bdev_path().find("/dev/block/dm-"), string::npos);
+ }
}
TEST_F(FiemapWriterTest, CheckFileCreated) {
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 2f516fa..dc3b985 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -401,8 +401,8 @@
// untouched to be compatible code that looks for an MBR. Thus we
// start counting free sectors at sector 1, not 0.
uint64_t free_area_start = LP_SECTOR_SIZE;
- if (out.alignment || out.alignment_offset) {
- free_area_start = AlignTo(free_area_start, out.alignment, out.alignment_offset);
+ if (out.alignment) {
+ free_area_start = AlignTo(free_area_start, out.alignment);
} else {
free_area_start = AlignTo(free_area_start, logical_block_size);
}
@@ -442,7 +442,7 @@
// Compute the first free sector, factoring in alignment.
uint64_t free_area_start = total_reserved;
if (super.alignment || super.alignment_offset) {
- free_area_start = AlignTo(free_area_start, super.alignment, super.alignment_offset);
+ free_area_start = AlignTo(free_area_start, super.alignment);
} else {
free_area_start = AlignTo(free_area_start, logical_block_size);
}
@@ -930,7 +930,7 @@
// Note: when reading alignment info from the Kernel, we don't assume it
// is aligned to the sector size, so we round up to the nearest sector.
uint64_t lba = sector * LP_SECTOR_SIZE;
- uint64_t aligned = AlignTo(lba, block_device.alignment, block_device.alignment_offset);
+ uint64_t aligned = AlignTo(lba, block_device.alignment);
return AlignTo(aligned, LP_SECTOR_SIZE) / LP_SECTOR_SIZE;
}
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index 977ebe3..52a3217 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -174,7 +174,7 @@
ASSERT_NE(exported, nullptr);
super_device = GetMetadataSuperBlockDevice(*exported.get());
ASSERT_NE(super_device, nullptr);
- EXPECT_EQ(super_device->first_logical_sector, 1472);
+ EXPECT_EQ(super_device->first_logical_sector, 1536);
// Alignment offset without alignment doesn't mean anything.
device_info.alignment = 0;
@@ -190,7 +190,7 @@
ASSERT_NE(exported, nullptr);
super_device = GetMetadataSuperBlockDevice(*exported.get());
ASSERT_NE(super_device, nullptr);
- EXPECT_EQ(super_device->first_logical_sector, 174);
+ EXPECT_EQ(super_device->first_logical_sector, 168);
// Test a small alignment with no alignment offset.
device_info.alignment = 11 * 1024;
@@ -200,7 +200,7 @@
ASSERT_NE(exported, nullptr);
super_device = GetMetadataSuperBlockDevice(*exported.get());
ASSERT_NE(super_device, nullptr);
- EXPECT_EQ(super_device->first_logical_sector, 160);
+ EXPECT_EQ(super_device->first_logical_sector, 154);
}
TEST_F(BuilderTest, InternalPartitionAlignment) {
@@ -229,12 +229,12 @@
EXPECT_EQ(extent.num_sectors, 80);
uint64_t lba = extent.target_data * LP_SECTOR_SIZE;
- uint64_t aligned_lba = AlignTo(lba, device_info.alignment, device_info.alignment_offset);
+ uint64_t aligned_lba = AlignTo(lba, device_info.alignment);
EXPECT_EQ(lba, aligned_lba);
}
// Sanity check one extent.
- EXPECT_EQ(exported->extents.back().target_data, 3008);
+ EXPECT_EQ(exported->extents.back().target_data, 3072);
}
TEST_F(BuilderTest, UseAllDiskSpace) {
@@ -652,7 +652,7 @@
};
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(partitions, "system_a", 65536, 2);
ASSERT_NE(builder, nullptr);
- EXPECT_EQ(builder->AllocatableSpace(), 467238912);
+ EXPECT_EQ(builder->AllocatableSpace(), 467402752);
// Create a partition that spans 3 devices.
Partition* p = builder->AddPartition("system_a", 0);
@@ -675,17 +675,17 @@
EXPECT_EQ(metadata->block_devices[2].alignment, 786432);
EXPECT_EQ(metadata->block_devices[2].alignment_offset, 753664);
ASSERT_EQ(metadata->extents.size(), 3);
- EXPECT_EQ(metadata->extents[0].num_sectors, 522304);
+ EXPECT_EQ(metadata->extents[0].num_sectors, 522752);
EXPECT_EQ(metadata->extents[0].target_type, LP_TARGET_TYPE_LINEAR);
- EXPECT_EQ(metadata->extents[0].target_data, 1984);
+ EXPECT_EQ(metadata->extents[0].target_data, 1536);
EXPECT_EQ(metadata->extents[0].target_source, 0);
- EXPECT_EQ(metadata->extents[1].num_sectors, 260672);
+ EXPECT_EQ(metadata->extents[1].num_sectors, 260608);
EXPECT_EQ(metadata->extents[1].target_type, LP_TARGET_TYPE_LINEAR);
- EXPECT_EQ(metadata->extents[1].target_data, 1472);
+ EXPECT_EQ(metadata->extents[1].target_data, 1536);
EXPECT_EQ(metadata->extents[1].target_source, 1);
- EXPECT_EQ(metadata->extents[2].num_sectors, 129088);
+ EXPECT_EQ(metadata->extents[2].num_sectors, 128704);
EXPECT_EQ(metadata->extents[2].target_type, LP_TARGET_TYPE_LINEAR);
- EXPECT_EQ(metadata->extents[2].target_data, 1472);
+ EXPECT_EQ(metadata->extents[2].target_data, 1536);
EXPECT_EQ(metadata->extents[2].target_source, 2);
}
@@ -1019,3 +1019,35 @@
EXPECT_FALSE(extent.OverlapsWith(LinearExtent{20, 1, 15}));
EXPECT_FALSE(extent.OverlapsWith(LinearExtent{20, 1, 10}));
}
+
+TEST_F(BuilderTest, AlignFreeRegion) {
+ BlockDeviceInfo super("super", 8_GiB, 786432, 0, 4096);
+ std::vector<BlockDeviceInfo> block_devices = {super};
+
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(block_devices, "super", 65536, 2);
+ ASSERT_NE(builder, nullptr);
+
+ Partition* p = builder->AddPartition("system", "default", 0);
+ ASSERT_NE(p, nullptr);
+ ASSERT_TRUE(builder->AddLinearExtent(p, "super", 64, (super.alignment + 4096) / 512));
+
+ p = builder->AddPartition("vendor", "default", 0);
+ ASSERT_NE(p, nullptr);
+ ASSERT_TRUE(builder->ResizePartition(p, 2_GiB));
+
+ const auto& extents = p->extents();
+ ASSERT_EQ(extents.size(), 2);
+
+ LinearExtent* e1 = extents[0]->AsLinearExtent();
+ ASSERT_NE(e1, nullptr);
+ LinearExtent* e2 = extents[1]->AsLinearExtent();
+ ASSERT_NE(e2, nullptr);
+
+ // The misaligned partition starting at sector 1544 should not cause any
+ // overlap with previous extents. We should see vendor punch a hole where
+ // "system" is, extending the hole up to the next aligned block.
+ EXPECT_EQ(e1->physical_sector(), 1536);
+ EXPECT_EQ(e1->end_sector(), 1544);
+ EXPECT_EQ(e2->physical_sector(), 3072);
+ EXPECT_EQ(e2->end_sector(), 4197368);
+}
diff --git a/fs_mgr/liblp/device_test.cpp b/fs_mgr/liblp/device_test.cpp
index 99bff6e..6af9d94 100644
--- a/fs_mgr/liblp/device_test.cpp
+++ b/fs_mgr/liblp/device_test.cpp
@@ -50,16 +50,7 @@
// Sanity check that the device doesn't give us some weird inefficient
// alignment.
EXPECT_EQ(device_info.alignment % LP_SECTOR_SIZE, 0);
- EXPECT_EQ(device_info.alignment_offset % LP_SECTOR_SIZE, 0);
- EXPECT_LE(device_info.alignment_offset, INT_MAX);
EXPECT_EQ(device_info.logical_block_size % LP_SECTOR_SIZE, 0);
-
- // Having an alignment offset > alignment doesn't really make sense.
- EXPECT_LT(device_info.alignment_offset, device_info.alignment);
-
- if (IPropertyFetcher::GetInstance()->GetBoolProperty("ro.virtual_ab.enabled", false)) {
- EXPECT_EQ(device_info.alignment_offset, 0);
- }
}
TEST_F(DeviceTest, ReadSuperPartitionCurrentSlot) {
diff --git a/fs_mgr/liblp/utility.h b/fs_mgr/liblp/utility.h
index 0661769..f210eaf 100644
--- a/fs_mgr/liblp/utility.h
+++ b/fs_mgr/liblp/utility.h
@@ -78,17 +78,6 @@
return base + (alignment - remainder);
}
-// Same as the above |AlignTo|, except that |base| is only aligned when added to
-// |alignment_offset|.
-constexpr uint64_t AlignTo(uint64_t base, uint32_t alignment, uint32_t alignment_offset) {
- uint64_t aligned = AlignTo(base, alignment) + alignment_offset;
- if (aligned - alignment >= base) {
- // We overaligned (base < alignment_offset).
- return aligned - alignment;
- }
- return aligned;
-}
-
// Update names from C++ strings.
bool UpdateBlockDevicePartitionName(LpMetadataBlockDevice* device, const std::string& name);
bool UpdatePartitionGroupName(LpMetadataPartitionGroup* group, const std::string& name);
diff --git a/fs_mgr/liblp/utility_test.cpp b/fs_mgr/liblp/utility_test.cpp
index cac3989..b64861d 100644
--- a/fs_mgr/liblp/utility_test.cpp
+++ b/fs_mgr/liblp/utility_test.cpp
@@ -64,9 +64,9 @@
EXPECT_EQ(AlignTo(555, 1024), 1024);
EXPECT_EQ(AlignTo(555, 1000), 1000);
EXPECT_EQ(AlignTo(0, 1024), 0);
- EXPECT_EQ(AlignTo(54, 32, 30), 62);
- EXPECT_EQ(AlignTo(32, 32, 30), 62);
- EXPECT_EQ(AlignTo(17, 32, 30), 30);
+ EXPECT_EQ(AlignTo(54, 32), 64);
+ EXPECT_EQ(AlignTo(32, 32), 32);
+ EXPECT_EQ(AlignTo(17, 32), 32);
}
TEST(liblp, GetPartitionSlotSuffix) {
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 384595d..40da1bc 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -100,6 +100,7 @@
cc_library_static {
name: "libsnapshot_init",
+ native_coverage : true,
defaults: ["libsnapshot_defaults"],
srcs: [":libsnapshot_sources"],
recovery_available: true,
@@ -160,23 +161,23 @@
"snapshot_test.cpp",
],
shared_libs: [
- "android.hardware.boot@1.0",
- "android.hardware.boot@1.1",
"libbinder",
"libcrypto",
"libhidlbase",
"libprotobuf-cpp-lite",
- "libsparse",
"libutils",
"libz",
],
static_libs: [
+ "android.hardware.boot@1.0",
+ "android.hardware.boot@1.1",
"libfs_mgr",
"libgsi",
"libgmock",
"liblp",
"libsnapshot",
"libsnapshot_test_helpers",
+ "libsparse",
],
header_libs: [
"libstorage_literals_headers",
@@ -244,3 +245,43 @@
],
gtest: false,
}
+
+cc_fuzz {
+ name: "libsnapshot_fuzzer",
+
+ // TODO(b/154633114): make host supported.
+ // host_supported: true,
+
+ native_coverage : true,
+ srcs: [
+ "snapshot_fuzz.cpp",
+ "snapshot_fuzz_utils.cpp",
+ "fuzz_utils.cpp",
+ ],
+ static_libs: [
+ "libbase",
+ "libcrypto_static",
+ "libcutils",
+ "libfs_mgr",
+ "libgtest", // from libsnapshot_test_helpers
+ "libgmock", // from libsnapshot_test_helpers
+ "liblog",
+ "liblp",
+ "libsnapshot_init", // don't use binder or hwbinder
+ "libsnapshot_test_helpers",
+ "libprotobuf-cpp-lite",
+ "update_metadata-protos",
+ ],
+ header_libs: [
+ "libstorage_literals_headers",
+ ],
+
+ fuzz_config: {
+ cc: ["android-virtual-ab+bugs@google.com"],
+ componentid: 30545,
+ hotlists: ["1646452"],
+ fuzz_on_haiku_host: false,
+ // TODO(b/154633114): set to true to run this automatically.
+ fuzz_on_haiku_device: false,
+ },
+}
diff --git a/fs_mgr/libsnapshot/fuzz.sh b/fs_mgr/libsnapshot/fuzz.sh
new file mode 100755
index 0000000..64d8224
--- /dev/null
+++ b/fs_mgr/libsnapshot/fuzz.sh
@@ -0,0 +1,88 @@
+#!/bin/bash
+PROJECT_PATH=system/core/fs_mgr/libsnapshot
+FUZZ_TARGET=libsnapshot_fuzzer
+TARGET_ARCH=$(get_build_var TARGET_ARCH)
+FUZZ_BINARY=/data/fuzz/${TARGET_ARCH}/${FUZZ_TARGET}/${FUZZ_TARGET}
+DEVICE_CORPSE_DIR=/data/local/tmp/${FUZZ_TARGET}
+DEVICE_GCOV_DIR=/data/local/tmp/${FUZZ_TARGET}/gcov
+HOST_SCRATCH_DIR=/tmp/${FUZZ_TARGET}
+GCOV_TOOL=${HOST_SCRATCH_DIR}/llvm-gcov
+
+build_normal() (
+ pushd $(gettop)
+ NATIVE_COVERAGE="" NATIVE_LINE_COVERAGE="" COVERAGE_PATHS="" m ${FUZZ_TARGET}
+ ret=$?
+ popd
+ return ${ret}
+)
+
+build_cov() {
+ pushd $(gettop)
+ ret=$?
+ NATIVE_COVERAGE="true" NATIVE_LINE_COVERAGE="true" COVERAGE_PATHS="${PROJECT_PATH}" m ${FUZZ_TARGET}
+ popd
+ return ${ret}
+}
+
+prepare_device() {
+ adb root && adb remount &&
+ adb shell mkdir -p ${DEVICE_CORPSE_DIR} &&
+ adb shell rm -rf ${DEVICE_GCOV_DIR} &&
+ adb shell mkdir -p ${DEVICE_GCOV_DIR}
+}
+
+push_binary() {
+ adb push ${ANDROID_PRODUCT_OUT}/${FUZZ_BINARY} ${FUZZ_BINARY}
+}
+
+prepare_host() {
+ which lcov || {
+ echo "please run:";
+ echo " sudo apt-get install lcov ";
+ return 1;
+ }
+ rm -rf ${HOST_SCRATCH_DIR} &&
+ mkdir -p ${HOST_SCRATCH_DIR}
+}
+
+# run_snapshot_fuzz -runs=10000
+generate_corpse() {
+ [[ "$@" ]] || { echo "run with -runs=X"; return 1; }
+
+ prepare_device &&
+ build_normal &&
+ push_binary &&
+ adb shell ${FUZZ_BINARY} "$@" ${DEVICE_CORPSE_DIR}
+}
+
+run_snapshot_fuzz() {
+ prepare_device &&
+ build_cov &&
+ push_binary &&
+ adb shell GCOV_PREFIX=${DEVICE_GCOV_DIR} GCOV_PREFIX_STRIP=3 \
+ ${FUZZ_BINARY} \
+ -runs=0 \
+ ${DEVICE_CORPSE_DIR}
+}
+
+show_fuzz_result() {
+ prepare_host &&
+ unzip -o -j -d ${HOST_SCRATCH_DIR} ${ANDROID_PRODUCT_OUT}/coverage/data/fuzz/${TARGET_ARCH}/${FUZZ_TARGET}/${FUZZ_TARGET}.zip &&
+ adb shell find ${DEVICE_GCOV_DIR} -type f | xargs -I {} adb pull {} ${HOST_SCRATCH_DIR} &&
+ ls ${HOST_SCRATCH_DIR} &&
+ cat > ${GCOV_TOOL} <<< '
+#!/bin/bash
+exec llvm-cov gcov "$@"
+' &&
+ chmod +x ${GCOV_TOOL} &&
+ lcov --directory ${HOST_SCRATCH_DIR} --base-directory $(gettop) --gcov-tool ${GCOV_TOOL} --capture -o ${HOST_SCRATCH_DIR}/report.cov &&
+ genhtml ${HOST_SCRATCH_DIR}/report.cov -o ${HOST_SCRATCH_DIR}/html &&
+ echo file://$(realpath ${HOST_SCRATCH_DIR}/html/index.html)
+}
+
+# run_snapshot_fuzz -runs=10000
+run_snapshot_fuzz_all() {
+ generate_corpse "$@" &&
+ run_snapshot_fuzz &&
+ show_fuzz_result
+}
diff --git a/fs_mgr/libsnapshot/fuzz_utils.cpp b/fs_mgr/libsnapshot/fuzz_utils.cpp
new file mode 100644
index 0000000..509eb1b
--- /dev/null
+++ b/fs_mgr/libsnapshot/fuzz_utils.cpp
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "fuzz_utils.h"
+
+#include <android-base/logging.h>
+
+namespace android::fuzz {
+
+void CheckInternal(bool value, std::string_view msg) {
+ CHECK(value) << msg;
+}
+
+} // namespace android::fuzz
diff --git a/fs_mgr/libsnapshot/fuzz_utils.h b/fs_mgr/libsnapshot/fuzz_utils.h
new file mode 100644
index 0000000..4e14b9c
--- /dev/null
+++ b/fs_mgr/libsnapshot/fuzz_utils.h
@@ -0,0 +1,267 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <optional>
+#include <string>
+#include <string_view>
+#include <vector>
+
+// Generic classes for fuzzing a collection of APIs.
+
+namespace android::fuzz {
+
+// My custom boolean type -- to avoid conflict with (u)int8_t and char.
+struct Bool {
+ bool value;
+ operator bool() const { return value; }
+};
+
+// Helper for FuzzData.
+// A wrapper over an optional const object T. The buffer is maintained elsewhere.
+template <typename T>
+class Optional {
+ public:
+ Optional(const T* ptr) : ptr_(ptr) {}
+ const T& operator*() const { return *ptr_; }
+ const T& value() const { return *ptr_; }
+ bool has_value() const { return ptr_; }
+
+ private:
+ const T* ptr_;
+};
+
+// Helper for FuzzData.
+// A wrapper over an optional boolean. The boolean is owned by this object.
+template <>
+class Optional<Bool> {
+ public:
+ Optional(std::optional<Bool>&& val) : val_(std::move(val)) {}
+ const Bool& operator*() const { return *val_; }
+ const Bool& value() const { return val_.value(); }
+ bool has_value() const { return val_.has_value(); }
+
+ private:
+ std::optional<Bool> val_;
+};
+
+// Helper for FuzzData.
+// A view on a raw data buffer. Client is responsible for maintaining the lifetime of the data
+// buffer.
+class DataView {
+ public:
+ DataView(const uint8_t* data, uint64_t size) : data_(data), size_(size) {}
+ DataView(const void* data, uint64_t size) : DataView(static_cast<const uint8_t*>(data), size) {}
+ inline uint64_t size() const { return size_; }
+ inline const uint8_t* data() const { return data_; }
+ inline bool CanConsume(uint64_t size) { return size_ >= size; }
+ // Consume the first |size| bytes from |this| and return a DataView object that represents
+ // the consumed data. Data pointer in |this| is incremented by |size| bytes.
+ // If not enough bytes, return nullopt.
+ std::optional<DataView> Consume(uint64_t size) {
+ if (!CanConsume(size)) return std::nullopt;
+ DataView ret(data_, size);
+ size_ -= size;
+ data_ += size;
+ return ret;
+ }
+
+ private:
+ const uint8_t* data_;
+ uint64_t size_;
+};
+
+// A view on the fuzz data. Provides APIs to consume typed objects.
+class FuzzData : public DataView {
+ public:
+ // Inherit constructors.
+ using DataView::DataView;
+ // Consume a data object T and return the pointer (into the buffer). No copy is done.
+ // If not enough bytes, return nullptr.
+ template <typename T>
+ inline Optional<T> Consume() {
+ auto data_view = DataView::Consume(sizeof(T));
+ if (!data_view.has_value()) return nullptr;
+ return reinterpret_cast<const T*>(data_view->data());
+ }
+ // To provide enough entropy for booleans, they are consumed bit by bit.
+ // Hence, the returned value is not indexed into the buffer. See Optional<Bool>.
+ template <>
+ Optional<Bool> Consume<Bool>() {
+ if (!boolean_buffer_.has_value() || boolean_bit_offset_ >= sizeof(*boolean_buffer_)) {
+ boolean_buffer_ = Consume<uint8_t>();
+ boolean_bit_offset_ = 0;
+ }
+ if (!boolean_buffer_.has_value()) {
+ return Optional<Bool>(std::nullopt);
+ }
+ const auto& byte = *boolean_buffer_;
+ bool ret = (byte >> boolean_bit_offset_) & 0x1;
+ boolean_bit_offset_++;
+ return Optional<Bool>(Bool{ret});
+ }
+
+ private:
+ // Separate buffer for booleans.
+ Optional<uint8_t> boolean_buffer_ = nullptr;
+ uint8_t boolean_bit_offset_ = 0;
+};
+
+enum class CallResult {
+ SUCCESS,
+ NOT_ENOUGH_DATA,
+};
+
+inline bool AllArgsHasValue() {
+ return true;
+}
+template <typename T>
+inline bool AllArgsHasValue(const Optional<T>& t) {
+ return t.has_value();
+}
+template <typename First, typename... Remaining>
+inline bool AllArgsHasValue(const Optional<First>& first, const Optional<Remaining>&... remaining) {
+ return first.has_value() && AllArgsHasValue(remaining...);
+}
+
+// Base class of FuzzFunction.
+class FuzzFunctionBase {
+ public:
+ virtual ~FuzzFunctionBase() = default;
+ virtual CallResult Call(FuzzData* fuzz_data) const = 0;
+};
+
+template <typename T>
+class FuzzFunction; // undefined
+
+// A wrapper over a fuzzed function.
+template <typename R, typename... Args>
+class FuzzFunction<R(Args...)> : public FuzzFunctionBase {
+ public:
+ using Function = std::function<R(Args...)>;
+ FuzzFunction(Function&& function) : function_(std::move(function)) {}
+ // Eat necessary data in |fuzz_data| and invoke the function.
+ CallResult Call(FuzzData* fuzz_data) const override {
+ return CallWithOptionalArgs(fuzz_data->Consume<std::remove_reference_t<Args>>()...);
+ }
+
+ private:
+ Function function_;
+
+ CallResult CallWithOptionalArgs(const Optional<std::remove_reference_t<Args>>&... args) const {
+ if (!AllArgsHasValue(args...)) {
+ return CallResult::NOT_ENOUGH_DATA;
+ }
+ (void)function_(args.value()...); // ignore returned value
+ return CallResult::SUCCESS;
+ }
+};
+
+// CHECK(value) << msg
+void CheckInternal(bool value, std::string_view msg);
+
+// A collection of FuzzFunction's.
+// FunctionsSizeType must be an integral type where
+// functions_.size() <= std::numeric_limits<FunctionSizeType>::max().
+template <typename FunctionsSizeType>
+class FuzzFunctions {
+ public:
+ // Subclass should override this to register functions via AddFunction.
+ FuzzFunctions() = default;
+ virtual ~FuzzFunctions() = default;
+ // Eat some amount of data in |fuzz_data| and call one of the |functions_|.
+ CallResult CallOne(FuzzData* fuzz_data) const {
+ auto opt_number = fuzz_data->Consume<FunctionsSizeType>();
+ if (!opt_number.has_value()) {
+ return CallResult::NOT_ENOUGH_DATA;
+ }
+ auto function_index = opt_number.value() % functions_.size();
+ return functions_[function_index]->Call(fuzz_data);
+ }
+
+ private:
+ template <typename R, typename... Args>
+ struct FunctionTraits {
+ using Function = std::function<R(Args...)>;
+ };
+
+ public:
+ // There are no deduction guide from lambda to std::function, so the
+ // signature of the lambda must be specified in the template argument.
+ // FuzzFunctions provide the following 3 ways to specify the signature of
+ // the lambda:
+
+ // AddFunction<R(Args...)>, e.g. AddFunction<ReturnType(ArgType, ArgType)>
+ template <typename T>
+ void AddFunction(std::function<T>&& func) {
+ functions_.push_back(std::make_unique<FuzzFunction<T>>(std::move(func)));
+ }
+
+ // AddFunction<R, Args...>, e.g. AddFunction<ReturnType, ArgType, ArgType>
+ template <typename R, typename... Args>
+ void AddFunction(typename FunctionTraits<R, Args...>::Function&& func) {
+ functions_.push_back(std::make_unique<FuzzFunction<R(Args...)>>(std::move(func)));
+ }
+
+ // AddFunction<ArgType...>. Equivalent to AddFunction<void, Args...>
+ template <typename... Args>
+ void AddFunction(typename FunctionTraits<void, Args...>::Function&& func) {
+ functions_.push_back(std::make_unique<FuzzFunction<void(Args...)>>(std::move(func)));
+ }
+
+ // Use |fuzz_data| as a guide to call |functions_| until |fuzz_data| is
+ // depleted. Return
+ void DepleteData(FuzzData* fuzz_data) const {
+ CallResult result;
+ while ((result = CallOne(fuzz_data)) == CallResult::SUCCESS)
+ ;
+ CheckInternal(result == CallResult::NOT_ENOUGH_DATA,
+ "result is " + std::to_string(static_cast<int>(result)));
+ }
+
+ protected:
+ // Helper for subclass to check that size of |functions_| is actually within
+ // SizeType. Should be called after all functions are registered.
+ void CheckFunctionsSize() const {
+ CheckInternal(functions_.size() <= std::numeric_limits<FunctionsSizeType>::max(),
+ "Need to extend number of bits for function count; there are " +
+ std::to_string(functions_.size()) + " functions now.");
+ }
+
+ private:
+ std::vector<std::unique_ptr<FuzzFunctionBase>> functions_;
+};
+
+// An object whose APIs are being fuzzed.
+template <typename T, typename FunctionsSizeType>
+class FuzzObject : public FuzzFunctions<FunctionsSizeType> {
+ public:
+ // Not thread-safe; client is responsible for ensuring only one thread calls DepleteData.
+ void DepleteData(T* obj, FuzzData* fuzz_data) {
+ obj_ = obj;
+ FuzzFunctions<FunctionsSizeType>::DepleteData(fuzz_data);
+ obj_ = nullptr;
+ }
+
+ protected:
+ // Helper for subclass to get the module under test in the added functions.
+ T* get() const {
+ CheckInternal(obj_ != nullptr, "No module under test is found.");
+ return obj_;
+ }
+
+ private:
+ T* obj_ = nullptr;
+};
+
+} // namespace android::fuzz
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index fff667e..8081866 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -318,6 +318,7 @@
friend class SnapshotUpdateTest;
friend class FlashAfterUpdateTest;
friend class LockTestConsumer;
+ friend class SnapshotFuzzEnv;
friend struct AutoDeleteCowImage;
friend struct AutoDeleteSnapshot;
friend struct PartitionCowCreator;
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz.cpp b/fs_mgr/libsnapshot/snapshot_fuzz.cpp
new file mode 100644
index 0000000..12a0531
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshot_fuzz.cpp
@@ -0,0 +1,141 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <stddef.h>
+#include <stdint.h>
+#include <sysexits.h>
+
+#include <functional>
+#include <sstream>
+#include <tuple>
+
+#include <android-base/logging.h>
+#include <storage_literals/storage_literals.h>
+
+#include "fuzz_utils.h"
+#include "snapshot_fuzz_utils.h"
+
+using android::base::LogId;
+using android::base::LogSeverity;
+using android::base::SetLogger;
+using android::base::StderrLogger;
+using android::base::StdioLogger;
+using android::fuzz::Bool;
+using android::fuzz::FuzzData;
+using android::fuzz::FuzzObject;
+using android::snapshot::SnapshotFuzzEnv;
+using android::snapshot::SnapshotManagerFuzzData;
+
+// Avoid linking to libgsi since it needs disk I/O.
+namespace android::gsi {
+bool IsGsiRunning() {
+ LOG(FATAL) << "Called IsGsiRunning";
+ __builtin_unreachable();
+}
+std::string GetDsuSlot(const std::string& install_dir) {
+ LOG(FATAL) << "Called GetDsuSlot(" << install_dir << ")";
+ __builtin_unreachable();
+}
+} // namespace android::gsi
+
+namespace android::snapshot {
+
+class FuzzSnapshotManager : public FuzzObject<ISnapshotManager, uint8_t> {
+ public:
+ FuzzSnapshotManager();
+};
+
+FuzzSnapshotManager::FuzzSnapshotManager() {
+ AddFunction([this]() { (void)get()->BeginUpdate(); });
+ AddFunction([this]() { (void)get()->CancelUpdate(); });
+ AddFunction<Bool>([this](Bool wipe) { (void)get()->FinishedSnapshotWrites(wipe); });
+ AddFunction([this]() { (void)get()->InitiateMerge(); });
+ AddFunction<Bool, Bool>([this](auto has_before_cancel, auto fail_before_cancel) {
+ std::function<bool()> before_cancel;
+ if (has_before_cancel) {
+ before_cancel = [=]() { return fail_before_cancel; };
+ }
+ (void)get()->ProcessUpdateState({}, before_cancel);
+ });
+ AddFunction<Bool>([this](auto has_progress_arg) {
+ double progress;
+ (void)get()->GetUpdateState(has_progress_arg ? &progress : nullptr);
+ });
+ // TODO add CreateUpdateSnapshots according to proto
+ // TODO add MapUpdateSnapshot
+ // TODO add UnmapUpdateSnapshot using names from the dictionary
+ AddFunction([this]() { (void)get()->NeedSnapshotsInFirstStageMount(); });
+ // TODO add CreateLogicalAndSnapshotPartitions
+ AddFunction<Bool>([this](const Bool& has_callback) {
+ std::function<void()> callback;
+ if (has_callback) {
+ callback = []() {};
+ }
+ (void)get()->HandleImminentDataWipe(callback);
+ });
+ AddFunction([this]() { (void)get()->RecoveryCreateSnapshotDevices(); });
+ // TODO add RecoveryCreateSnapshotDevices with metadata_device arg
+ AddFunction([this]() {
+ std::stringstream ss;
+ (void)get()->Dump(ss);
+ });
+ AddFunction([this]() { (void)get()->EnsureMetadataMounted(); });
+ AddFunction([this]() { (void)get()->GetSnapshotMergeStatsInstance(); });
+
+ CheckFunctionsSize();
+}
+
+// During global init, log all messages to stdio. This is only done once.
+int AllowLoggingDuringGlobalInit() {
+ SetLogger(&StdioLogger);
+ return 0;
+}
+
+// Only log fatal messages during tests.
+void FatalOnlyLogger(LogId logid, LogSeverity severity, const char* tag, const char* file,
+ unsigned int line, const char* message) {
+ if (severity == LogSeverity::FATAL) {
+ StderrLogger(logid, severity, tag, file, line, message);
+ }
+}
+// Stop logging (except fatal messages) after global initialization. This is only done once.
+int StopLoggingAfterGlobalInit() {
+ SetLogger(&FatalOnlyLogger);
+ return 0;
+}
+
+} // namespace android::snapshot
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ using namespace android::snapshot;
+
+ [[maybe_unused]] static auto allow_logging = AllowLoggingDuringGlobalInit();
+ static SnapshotFuzzEnv env;
+ static FuzzSnapshotManager fuzz_snapshot_manager;
+ [[maybe_unused]] static auto stop_logging = StopLoggingAfterGlobalInit();
+
+ CHECK(env.InitOk());
+ FuzzData fuzz_data(data, size);
+
+ auto snapshot_manager_data = fuzz_data.Consume<SnapshotManagerFuzzData>();
+ if (!snapshot_manager_data.has_value()) {
+ return 0;
+ }
+ auto snapshot_manager = env.CreateSnapshotManager(snapshot_manager_data.value());
+ CHECK(snapshot_manager);
+
+ fuzz_snapshot_manager.DepleteData(snapshot_manager.get(), &fuzz_data);
+
+ return 0;
+}
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp b/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp
new file mode 100644
index 0000000..d5e3e96
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp
@@ -0,0 +1,227 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <ftw.h>
+#include <inttypes.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sysexits.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <libsnapshot/auto_device.h>
+#include <libsnapshot/snapshot.h>
+#include <storage_literals/storage_literals.h>
+
+#include "snapshot_fuzz_utils.h"
+
+using namespace android::storage_literals;
+using namespace std::string_literals;
+
+using android::base::StringPrintf;
+using android::base::unique_fd;
+using android::base::WriteStringToFile;
+using android::fiemap::IImageManager;
+using android::fiemap::ImageManager;
+
+static const char MNT_DIR[] = "/mnt";
+static const char FAKE_ROOT_NAME[] = "snapshot_fuzz";
+static const auto SUPER_IMAGE_SIZE = 16_MiB;
+static const auto FAKE_ROOT_SIZE = 64_MiB;
+
+namespace android::snapshot {
+
+bool Mkdir(const std::string& path) {
+ if (mkdir(path.c_str(), 0750) == -1 && errno != EEXIST) {
+ PLOG(ERROR) << "Cannot create " << path;
+ return false;
+ }
+ return true;
+}
+
+bool RmdirRecursive(const std::string& path) {
+ auto callback = [](const char* child, const struct stat*, int file_type, struct FTW*) -> int {
+ switch (file_type) {
+ case FTW_D:
+ case FTW_DP:
+ case FTW_DNR:
+ if (rmdir(child) == -1) {
+ PLOG(ERROR) << "rmdir " << child;
+ return -1;
+ }
+ return 0;
+ case FTW_NS:
+ default:
+ if (rmdir(child) != -1) break;
+ [[fallthrough]];
+ case FTW_F:
+ case FTW_SL:
+ case FTW_SLN:
+ if (unlink(child) == -1) {
+ PLOG(ERROR) << "unlink " << child;
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+ };
+
+ return nftw(path.c_str(), callback, 128, FTW_DEPTH | FTW_MOUNT | FTW_PHYS) == 0;
+}
+
+class AutoDeleteDir : public AutoDevice {
+ public:
+ static std::unique_ptr<AutoDeleteDir> New(const std::string& path) {
+ if (!Mkdir(path)) {
+ return std::unique_ptr<AutoDeleteDir>(new AutoDeleteDir(""));
+ }
+ return std::unique_ptr<AutoDeleteDir>(new AutoDeleteDir(path));
+ }
+ ~AutoDeleteDir() {
+ if (!HasDevice()) return;
+ if (rmdir(name_.c_str()) == -1) {
+ PLOG(ERROR) << "Cannot remove " << name_;
+ }
+ }
+
+ private:
+ AutoDeleteDir(const std::string& path) : AutoDevice(path) {}
+};
+
+class AutoUnmount : public AutoDevice {
+ public:
+ static std::unique_ptr<AutoUnmount> New(const std::string& path, uint64_t size) {
+ if (mount("tmpfs", path.c_str(), "tmpfs", 0,
+ (void*)StringPrintf("size=%" PRIu64, size).data()) == -1) {
+ PLOG(ERROR) << "Cannot mount " << path;
+ return std::unique_ptr<AutoUnmount>(new AutoUnmount(""));
+ }
+ return std::unique_ptr<AutoUnmount>(new AutoUnmount(path));
+ }
+ ~AutoUnmount() {
+ if (!HasDevice()) return;
+ if (umount(name_.c_str()) == -1) {
+ PLOG(ERROR) << "Cannot umount " << name_;
+ }
+ }
+
+ private:
+ AutoUnmount(const std::string& path) : AutoDevice(path) {}
+};
+
+// A directory on tmpfs. Upon destruct, it is unmounted and deleted.
+class AutoMemBasedDir : public AutoDevice {
+ public:
+ static std::unique_ptr<AutoMemBasedDir> New(const std::string& name, uint64_t size) {
+ auto ret = std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(name));
+ ret->auto_delete_mount_dir_ = AutoDeleteDir::New(ret->mount_path());
+ if (!ret->auto_delete_mount_dir_->HasDevice()) {
+ return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
+ }
+ ret->auto_umount_mount_point_ = AutoUnmount::New(ret->mount_path(), size);
+ if (!ret->auto_umount_mount_point_->HasDevice()) {
+ return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
+ }
+ // path() does not need to be deleted upon destruction, hence it is not wrapped with
+ // AutoDeleteDir.
+ if (!Mkdir(ret->path())) {
+ return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
+ }
+ return ret;
+ }
+ // Return the scratch directory.
+ std::string path() const {
+ CHECK(HasDevice());
+ return mount_path() + "/root";
+ }
+ // Delete all contents in path() and start over. path() itself is re-created.
+ bool SoftReset() { return RmdirRecursive(path()) && Mkdir(path()); }
+
+ private:
+ AutoMemBasedDir(const std::string& name) : AutoDevice(name) {}
+ std::string mount_path() const {
+ CHECK(HasDevice());
+ return MNT_DIR + "/"s + name_;
+ }
+ std::unique_ptr<AutoDeleteDir> auto_delete_mount_dir_;
+ std::unique_ptr<AutoUnmount> auto_umount_mount_point_;
+};
+
+SnapshotFuzzEnv::SnapshotFuzzEnv() {
+ fake_root_ = AutoMemBasedDir::New(FAKE_ROOT_NAME, FAKE_ROOT_SIZE);
+}
+
+SnapshotFuzzEnv::~SnapshotFuzzEnv() = default;
+
+bool SnapshotFuzzEnv::InitOk() const {
+ if (fake_root_ == nullptr || !fake_root_->HasDevice()) return false;
+ return true;
+}
+
+bool SnapshotFuzzEnv::SoftReset() {
+ return fake_root_->SoftReset();
+}
+
+std::unique_ptr<IImageManager> SnapshotFuzzEnv::CreateFakeImageManager(
+ const std::string& fake_root) {
+ auto images_dir = fake_root + "/images";
+ auto metadata_dir = images_dir + "/metadata";
+ auto data_dir = images_dir + "/data";
+
+ if (!Mkdir(images_dir) || !Mkdir(metadata_dir) || !Mkdir(data_dir)) {
+ return nullptr;
+ }
+ return ImageManager::Open(metadata_dir, data_dir);
+}
+
+std::unique_ptr<TestPartitionOpener> SnapshotFuzzEnv::CreatePartitionOpener(
+ const std::string& fake_root) {
+ auto fake_super = fake_root + "/super.img";
+ std::string zeros(SUPER_IMAGE_SIZE, '\0');
+
+ if (!WriteStringToFile(zeros, fake_super)) {
+ PLOG(ERROR) << "Cannot write zeros to " << fake_super;
+ return nullptr;
+ }
+
+ return std::make_unique<TestPartitionOpener>(fake_super);
+}
+
+std::string SnapshotFuzzEnv::root() const {
+ CHECK(InitOk());
+ return fake_root_->path();
+}
+
+std::unique_ptr<ISnapshotManager> SnapshotFuzzEnv::CreateSnapshotManager(
+ const SnapshotManagerFuzzData& data) {
+ // TODO(b/154633114): create valid super partition according to fuzz data
+ auto partition_opener = CreatePartitionOpener(root());
+ if (partition_opener == nullptr) return nullptr;
+ auto metadata_dir = root() + "/snapshot_metadata";
+ if (!Mkdir(metadata_dir)) return nullptr;
+
+ auto device_info = new SnapshotFuzzDeviceInfo(data.device_info_data,
+ std::move(partition_opener), metadata_dir);
+ auto snapshot = SnapshotManager::New(device_info /* takes ownership */);
+ snapshot->images_ = CreateFakeImageManager(root());
+ snapshot->has_local_image_manager_ = data.is_local_image_manager;
+
+ return snapshot;
+}
+
+} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
new file mode 100644
index 0000000..32910a9
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
@@ -0,0 +1,115 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <libfiemap/image_manager.h>
+#include <libsnapshot/auto_device.h>
+#include <libsnapshot/test_helpers.h>
+
+// libsnapshot-specific code for fuzzing. Defines fake classes that are depended
+// by SnapshotManager.
+
+namespace android::snapshot {
+
+// Controls the behavior of IDeviceInfo.
+typedef struct SnapshotFuzzDeviceInfoData {
+ bool slot_suffix_is_a : 1;
+ bool is_overlayfs_setup : 1;
+ bool allow_set_boot_control_merge_status : 1;
+ bool allow_set_slot_as_unbootable : 1;
+ bool is_recovery : 1;
+} __attribute__((packed)) SnapshotFuzzDeviceInfoData;
+
+// Controls the behavior of the test SnapshotManager.
+typedef struct SnapshotManagerFuzzData {
+ SnapshotFuzzDeviceInfoData device_info_data;
+ bool is_local_image_manager : 1;
+} __attribute__((packed)) SnapshotManagerFuzzData;
+
+class AutoMemBasedDir;
+
+// Prepare test environment. This has a heavy overhead and should be done once.
+class SnapshotFuzzEnv {
+ public:
+ // Check if test should run at all.
+ static bool ShouldSkipTest();
+
+ // Initialize the environment.
+ SnapshotFuzzEnv();
+ ~SnapshotFuzzEnv();
+
+ // Check if environment is initialized properly.
+ bool InitOk() const;
+
+ // A scratch directory for the test to play around with. The scratch directory
+ // is backed by tmpfs. SoftReset() clears the directory.
+ std::string root() const;
+
+ // Soft reset part of the environment before running the next test.
+ bool SoftReset();
+
+ // Create a snapshot manager for this test run.
+ // Client is responsible for maintaining the lifetime of |data| over the life time of
+ // ISnapshotManager.
+ std::unique_ptr<ISnapshotManager> CreateSnapshotManager(const SnapshotManagerFuzzData& data);
+
+ private:
+ std::unique_ptr<AutoMemBasedDir> fake_root_;
+
+ static std::unique_ptr<android::fiemap::IImageManager> CreateFakeImageManager(
+ const std::string& fake_root);
+ static std::unique_ptr<TestPartitionOpener> CreatePartitionOpener(const std::string& fake_root);
+};
+
+class SnapshotFuzzDeviceInfo : public ISnapshotManager::IDeviceInfo {
+ public:
+ // Client is responsible for maintaining the lifetime of |data|.
+ SnapshotFuzzDeviceInfo(const SnapshotFuzzDeviceInfoData& data,
+ std::unique_ptr<TestPartitionOpener>&& partition_opener,
+ const std::string& metadata_dir)
+ : data_(data),
+ partition_opener_(std::move(partition_opener)),
+ metadata_dir_(metadata_dir) {}
+
+ // Following APIs are mocked.
+ std::string GetGsidDir() const override { return "fuzz_ota"; }
+ std::string GetMetadataDir() const override { return metadata_dir_; }
+ std::string GetSuperDevice(uint32_t) const override {
+ // TestPartitionOpener can recognize this.
+ return "super";
+ }
+ const android::fs_mgr::IPartitionOpener& GetPartitionOpener() const override {
+ return *partition_opener_;
+ }
+
+ // Following APIs are fuzzed.
+ std::string GetSlotSuffix() const override { return data_.slot_suffix_is_a ? "_a" : "_b"; }
+ std::string GetOtherSlotSuffix() const override { return data_.slot_suffix_is_a ? "_b" : "_a"; }
+ bool IsOverlayfsSetup() const override { return data_.is_overlayfs_setup; }
+ bool SetBootControlMergeStatus(android::hardware::boot::V1_1::MergeStatus) override {
+ return data_.allow_set_boot_control_merge_status;
+ }
+ bool SetSlotAsUnbootable(unsigned int) override { return data_.allow_set_slot_as_unbootable; }
+ bool IsRecovery() const override { return data_.is_recovery; }
+
+ private:
+ SnapshotFuzzDeviceInfoData data_;
+ std::unique_ptr<TestPartitionOpener> partition_opener_;
+ std::string metadata_dir_;
+};
+
+} // namespace android::snapshot
diff --git a/init/init.cpp b/init/init.cpp
index a9d6301..b177495 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -76,6 +76,7 @@
#include "service.h"
#include "service_parser.h"
#include "sigchld_handler.h"
+#include "subcontext.h"
#include "system/core/init/property_service.pb.h"
#include "util.h"
@@ -100,8 +101,6 @@
static int signal_fd = -1;
static int property_fd = -1;
-static std::unique_ptr<Subcontext> subcontext;
-
struct PendingControlMessage {
std::string message;
std::string name;
@@ -279,9 +278,8 @@
Parser parser;
parser.AddSectionParser("service", std::make_unique<ServiceParser>(
- &service_list, subcontext.get(), std::nullopt));
- parser.AddSectionParser("on",
- std::make_unique<ActionParser>(&action_manager, subcontext.get()));
+ &service_list, GetSubcontext(), std::nullopt));
+ parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, GetSubcontext()));
parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
return parser;
@@ -291,9 +289,9 @@
Parser CreateServiceOnlyParser(ServiceList& service_list, bool from_apex) {
Parser parser;
- parser.AddSectionParser("service",
- std::make_unique<ServiceParser>(&service_list, subcontext.get(),
- std::nullopt, from_apex));
+ parser.AddSectionParser(
+ "service", std::make_unique<ServiceParser>(&service_list, GetSubcontext(), std::nullopt,
+ from_apex));
return parser;
}
@@ -809,7 +807,7 @@
PLOG(FATAL) << "SetupMountNamespaces failed";
}
- subcontext = InitializeSubcontext();
+ InitializeSubcontext();
ActionManager& am = ActionManager::GetInstance();
ServiceList& sm = ServiceList::GetInstance();
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index 5263c14..f3dd538 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -52,6 +52,8 @@
namespace {
std::string shutdown_command;
+static bool subcontext_terminated_by_shutdown;
+static std::unique_ptr<Subcontext> subcontext;
class SubcontextProcess {
public:
@@ -323,34 +325,30 @@
return expanded_args;
}
-static std::vector<Subcontext> subcontexts;
-static bool shutting_down;
-
-std::unique_ptr<Subcontext> InitializeSubcontext() {
+void InitializeSubcontext() {
if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_P__) {
- return std::make_unique<Subcontext>(std::vector<std::string>{"/vendor", "/odm"},
- kVendorContext);
+ subcontext.reset(
+ new Subcontext(std::vector<std::string>{"/vendor", "/odm"}, kVendorContext));
}
- return nullptr;
+}
+
+Subcontext* GetSubcontext() {
+ return subcontext.get();
}
bool SubcontextChildReap(pid_t pid) {
- for (auto& subcontext : subcontexts) {
- if (subcontext.pid() == pid) {
- if (!shutting_down) {
- subcontext.Restart();
- }
- return true;
+ if (subcontext->pid() == pid) {
+ if (!subcontext_terminated_by_shutdown) {
+ subcontext->Restart();
}
+ return true;
}
return false;
}
void SubcontextTerminate() {
- shutting_down = true;
- for (auto& subcontext : subcontexts) {
- kill(subcontext.pid(), SIGTERM);
- }
+ subcontext_terminated_by_shutdown = true;
+ kill(subcontext->pid(), SIGTERM);
}
} // namespace init
diff --git a/init/subcontext.h b/init/subcontext.h
index 5e1d8a8..788d3be 100644
--- a/init/subcontext.h
+++ b/init/subcontext.h
@@ -60,7 +60,8 @@
};
int SubcontextMain(int argc, char** argv, const BuiltinFunctionMap* function_map);
-std::unique_ptr<Subcontext> InitializeSubcontext();
+void InitializeSubcontext();
+Subcontext* GetSubcontext();
bool SubcontextChildReap(pid_t pid);
void SubcontextTerminate();
diff --git a/libcutils/include/cutils/ashmem.h b/libcutils/include/cutils/ashmem.h
index d80caa6..1913c1e 100644
--- a/libcutils/include/cutils/ashmem.h
+++ b/libcutils/include/cutils/ashmem.h
@@ -1,14 +1,20 @@
-/* cutils/ashmem.h
- **
- ** Copyright 2008 The Android Open Source Project
- **
- ** This file is dual licensed. It may be redistributed and/or modified
- ** under the terms of the Apache 2.0 License OR version 2 of the GNU
- ** General Public License.
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
-#ifndef _CUTILS_ASHMEM_H
-#define _CUTILS_ASHMEM_H
+#pragma once
#include <stddef.h>
@@ -30,5 +36,3 @@
#ifdef __cplusplus
}
#endif
-
-#endif /* _CUTILS_ASHMEM_H */
diff --git a/liblog/Android.bp b/liblog/Android.bp
index 0b98e1a..6051ac7 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -42,6 +42,7 @@
"//apex_available:platform",
"//apex_available:anyapex",
],
+ min_sdk_version: "29",
native_bridge_supported: true,
export_include_dirs: ["include"],
system_shared_libs: [],
diff --git a/liblog/include/log/log.h b/liblog/include/log/log.h
index 19edb83..820b7cb 100644
--- a/liblog/include/log/log.h
+++ b/liblog/include/log/log.h
@@ -29,7 +29,6 @@
#include <log/log_id.h>
#include <log/log_main.h>
#include <log/log_radio.h>
-#include <log/log_read.h>
#include <log/log_safetynet.h>
#include <log/log_system.h>
#include <log/log_time.h>
@@ -65,6 +64,13 @@
#endif
/*
+ * The maximum size of the log entry payload that can be
+ * written to the logger. An attempt to write more than
+ * this amount will result in a truncated log entry.
+ */
+#define LOGGER_ENTRY_MAX_PAYLOAD 4068
+
+/*
* Event logging.
*/
@@ -138,8 +144,11 @@
/*
* Release any logger resources (a new log write will immediately re-acquire)
*
- * May be used to clean up File descriptors after a Fork, the resources are
- * all O_CLOEXEC so wil self clean on exec().
+ * This is specifically meant to be used by Zygote to close open file descriptors after fork()
+ * and before specialization. O_CLOEXEC is used on file descriptors, so they will be closed upon
+ * exec() in normal use cases.
+ *
+ * Note that this is not safe to call from a multi-threaded program.
*/
void __android_log_close(void);
diff --git a/liblog/include/log/log_properties.h b/liblog/include/log/log_properties.h
index 3a8af6d..3497d63 100644
--- a/liblog/include/log/log_properties.h
+++ b/liblog/include/log/log_properties.h
@@ -1,11 +1,18 @@
/*
-**
-** Copyright 2017, The Android Open Source Project
-**
-** This file is dual licensed. It may be redistributed and/or modified
-** under the terms of the Apache 2.0 License OR version 2 of the GNU
-** General Public License.
-*/
+ * Copyright (C) 2017 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
diff --git a/liblog/include/log/log_read.h b/liblog/include/log/log_read.h
index e2bc297..ffd3b52 100644
--- a/liblog/include/log/log_read.h
+++ b/liblog/include/log/log_read.h
@@ -48,13 +48,6 @@
};
/*
- * The maximum size of the log entry payload that can be
- * written to the logger. An attempt to write more than
- * this amount will result in a truncated log entry.
- */
-#define LOGGER_ENTRY_MAX_PAYLOAD 4068
-
-/*
* The maximum size of a log entry which can be read.
* An attempt to read less than this amount may result
* in read() returning EINVAL.
diff --git a/liblog/include/log/log_safetynet.h b/liblog/include/log/log_safetynet.h
index d3e9b19..b2604b5 100644
--- a/liblog/include/log/log_safetynet.h
+++ b/liblog/include/log/log_safetynet.h
@@ -1,11 +1,18 @@
/*
-**
-** Copyright 2017, The Android Open Source Project
-**
-** This file is dual licensed. It may be redistributed and/or modified
-** under the terms of the Apache 2.0 License OR version 2 of the GNU
-** General Public License.
-*/
+ * Copyright (C) 2017 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
diff --git a/liblog/logd_writer.cpp b/liblog/logd_writer.cpp
index 67376f4..a230749 100644
--- a/liblog/logd_writer.cpp
+++ b/liblog/logd_writer.cpp
@@ -32,58 +32,53 @@
#include <time.h>
#include <unistd.h>
-#include <shared_mutex>
-
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
#include "logger.h"
-#include "rwlock.h"
#include "uio.h"
-static int logd_socket;
-static RwLock logd_socket_lock;
+static atomic_int logd_socket;
-static void OpenSocketLocked() {
- logd_socket = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
- if (logd_socket <= 0) {
- return;
- }
-
+// Note that it is safe to call connect() multiple times on DGRAM Unix domain sockets, so this
+// function is used to reconnect to logd without requiring a new socket.
+static void LogdConnect() {
sockaddr_un un = {};
un.sun_family = AF_UNIX;
strcpy(un.sun_path, "/dev/socket/logdw");
-
- if (TEMP_FAILURE_RETRY(
- connect(logd_socket, reinterpret_cast<sockaddr*>(&un), sizeof(sockaddr_un))) < 0) {
- close(logd_socket);
- logd_socket = 0;
- }
+ TEMP_FAILURE_RETRY(connect(logd_socket, reinterpret_cast<sockaddr*>(&un), sizeof(sockaddr_un)));
}
-static void OpenSocket() {
- auto lock = std::unique_lock{logd_socket_lock};
- if (logd_socket > 0) {
- // Someone raced us and opened the socket already.
+// logd_socket should only be opened once. If we see that logd_socket is uninitialized, we create a
+// new socket and attempt to exchange it into the atomic logd_socket. If the compare/exchange was
+// successful, then that will be the socket used for the duration of the program, otherwise a
+// different thread has already opened and written the socket to the atomic, so close the new socket
+// and return.
+static void GetSocket() {
+ if (logd_socket != 0) {
return;
}
- OpenSocketLocked();
-}
-
-static void ResetSocket(int old_socket) {
- auto lock = std::unique_lock{logd_socket_lock};
- if (old_socket != logd_socket) {
- // Someone raced us and reset the socket already.
+ int new_socket =
+ TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
+ if (new_socket <= 0) {
return;
}
- close(logd_socket);
- logd_socket = 0;
- OpenSocketLocked();
+
+ int uninitialized_value = 0;
+ if (!logd_socket.compare_exchange_strong(uninitialized_value, new_socket)) {
+ close(new_socket);
+ return;
+ }
+
+ LogdConnect();
}
+// This is the one exception to the above. Zygote uses this to clean up open FD's after fork() and
+// before specialization. It is single threaded at this point and therefore this function is
+// explicitly not thread safe. It sets logd_socket to 0, so future logs will be safely initialized
+// whenever they happen.
void LogdClose() {
- auto lock = std::unique_lock{logd_socket_lock};
if (logd_socket > 0) {
close(logd_socket);
}
@@ -99,12 +94,7 @@
static atomic_int dropped;
static atomic_int droppedSecurity;
- auto lock = std::shared_lock{logd_socket_lock};
- if (logd_socket <= 0) {
- lock.unlock();
- OpenSocket();
- lock.lock();
- }
+ GetSocket();
if (logd_socket <= 0) {
return -EBADF;
@@ -183,10 +173,7 @@
// the connection, so we reset it and try again.
ret = TEMP_FAILURE_RETRY(writev(logd_socket, newVec, i));
if (ret < 0 && errno != EAGAIN) {
- int old_socket = logd_socket;
- lock.unlock();
- ResetSocket(old_socket);
- lock.lock();
+ LogdConnect();
ret = TEMP_FAILURE_RETRY(writev(logd_socket, newVec, i));
}
diff --git a/liblog/logprint.cpp b/liblog/logprint.cpp
index 5c69bf8..9e8d277 100644
--- a/liblog/logprint.cpp
+++ b/liblog/logprint.cpp
@@ -19,6 +19,8 @@
#define HAVE_STRSEP
#endif
+#include <log/logprint.h>
+
#include <assert.h>
#include <ctype.h>
#include <errno.h>
@@ -37,7 +39,7 @@
#include <cutils/list.h>
#include <log/log.h>
-#include <log/logprint.h>
+#include <log/log_read.h>
#include <private/android_logger.h>
#define MS_PER_NSEC 1000000
diff --git a/liblog/pmsg_writer.cpp b/liblog/pmsg_writer.cpp
index 06e5e04..0751e2c 100644
--- a/liblog/pmsg_writer.cpp
+++ b/liblog/pmsg_writer.cpp
@@ -23,30 +23,36 @@
#include <sys/types.h>
#include <time.h>
-#include <shared_mutex>
-
#include <log/log_properties.h>
#include <private/android_logger.h>
#include "logger.h"
-#include "rwlock.h"
#include "uio.h"
-static int pmsg_fd;
-static RwLock pmsg_fd_lock;
+static atomic_int pmsg_fd;
-static void PmsgOpen() {
- auto lock = std::unique_lock{pmsg_fd_lock};
- if (pmsg_fd > 0) {
- // Someone raced us and opened the socket already.
+// pmsg_fd should only beopened once. If we see that pmsg_fd is uninitialized, we open "/dev/pmsg0"
+// then attempt to compare/exchange it into pmsg_fd. If the compare/exchange was successful, then
+// that will be the fd used for the duration of the program, otherwise a different thread has
+// already opened and written the fd to the atomic, so close the new fd and return.
+static void GetPmsgFd() {
+ if (pmsg_fd != 0) {
return;
}
- pmsg_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
+ int new_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
+ if (new_fd <= 0) {
+ return;
+ }
+
+ int uninitialized_value = 0;
+ if (!pmsg_fd.compare_exchange_strong(uninitialized_value, new_fd)) {
+ close(new_fd);
+ return;
+ }
}
void PmsgClose() {
- auto lock = std::unique_lock{pmsg_fd_lock};
if (pmsg_fd > 0) {
close(pmsg_fd);
}
@@ -77,13 +83,7 @@
}
}
- auto lock = std::shared_lock{pmsg_fd_lock};
-
- if (pmsg_fd <= 0) {
- lock.unlock();
- PmsgOpen();
- lock.lock();
- }
+ GetPmsgFd();
if (pmsg_fd <= 0) {
return -EBADF;
diff --git a/liblog/rwlock.h b/liblog/rwlock.h
deleted file mode 100644
index 00f1806..0000000
--- a/liblog/rwlock.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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
-
-#include <pthread.h>
-
-// As of the end of Dec 2019, std::shared_mutex is *not* simply a pthread_rwlock, but rather a
-// combination of std::mutex and std::condition variable, which is obviously less efficient. This
-// immitates what std::shared_mutex should be doing and is compatible with std::shared_lock and
-// std::unique_lock.
-
-class RwLock {
- public:
- RwLock() {}
- ~RwLock() {}
-
- void lock() { pthread_rwlock_wrlock(&rwlock_); }
- void unlock() { pthread_rwlock_unlock(&rwlock_); }
-
- void lock_shared() { pthread_rwlock_rdlock(&rwlock_); }
- void unlock_shared() { pthread_rwlock_unlock(&rwlock_); }
-
- private:
- pthread_rwlock_t rwlock_ = PTHREAD_RWLOCK_INITIALIZER;
-};
diff --git a/liblog/tests/Android.bp b/liblog/tests/Android.bp
index 385b079..50800c5 100644
--- a/liblog/tests/Android.bp
+++ b/liblog/tests/Android.bp
@@ -63,8 +63,8 @@
"log_system_test.cpp",
"log_time_test.cpp",
"log_wrap_test.cpp",
+ "logd_writer_test.cpp",
"logprint_test.cpp",
- "rwlock_test.cpp",
],
shared_libs: [
"libcutils",
@@ -108,7 +108,6 @@
"liblog_host_test.cpp",
"liblog_default_tag.cpp",
"liblog_global_state.cpp",
- "rwlock_test.cpp",
],
isolated: true,
}
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index 3a6ed90..f4734b9 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -31,6 +31,7 @@
#include <benchmark/benchmark.h>
#include <cutils/sockets.h>
#include <log/event_tag_map.h>
+#include <log/log_read.h>
#include <private/android_logger.h>
BENCHMARK_MAIN();
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 048bf61..d3d8e91 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -40,6 +40,7 @@
#include <gtest/gtest.h>
#include <log/log_event_list.h>
#include <log/log_properties.h>
+#include <log/log_read.h>
#include <log/logprint.h>
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
diff --git a/liblog/tests/logd_writer_test.cpp b/liblog/tests/logd_writer_test.cpp
new file mode 100644
index 0000000..c856720
--- /dev/null
+++ b/liblog/tests/logd_writer_test.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+
+using android::base::StringPrintf;
+using android::base::unique_fd;
+
+// logd_writer takes advantage of the fact that connect() can be called multiple times for a DGRAM
+// socket. This tests for that behavior.
+TEST(liblog, multi_connect_dgram_socket) {
+#ifdef __ANDROID__
+ auto temp_dir = TemporaryDir();
+ auto socket_path = StringPrintf("%s/test_socket", temp_dir.path);
+
+ unique_fd server_socket;
+
+ auto open_server_socket = [&] {
+ server_socket.reset(TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0)));
+ ASSERT_TRUE(server_socket.ok());
+
+ sockaddr_un server_sockaddr = {};
+ server_sockaddr.sun_family = AF_UNIX;
+ strlcpy(server_sockaddr.sun_path, socket_path.c_str(), sizeof(server_sockaddr.sun_path));
+ ASSERT_EQ(0,
+ TEMP_FAILURE_RETRY(bind(server_socket, reinterpret_cast<sockaddr*>(&server_sockaddr),
+ sizeof(server_sockaddr))));
+ };
+
+ // Open the server socket.
+ open_server_socket();
+
+ // Open the client socket.
+ auto client_socket =
+ unique_fd{TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0))};
+ ASSERT_TRUE(client_socket.ok());
+ sockaddr_un client_sockaddr = {};
+ client_sockaddr.sun_family = AF_UNIX;
+ strlcpy(client_sockaddr.sun_path, socket_path.c_str(), sizeof(client_sockaddr.sun_path));
+ ASSERT_EQ(0,
+ TEMP_FAILURE_RETRY(connect(client_socket, reinterpret_cast<sockaddr*>(&client_sockaddr),
+ sizeof(client_sockaddr))));
+
+ // Ensure that communication works.
+ constexpr static char kSmoke[] = "smoke test";
+ ssize_t smoke_len = sizeof(kSmoke);
+ ASSERT_EQ(smoke_len, TEMP_FAILURE_RETRY(write(client_socket, kSmoke, sizeof(kSmoke))));
+ char read_buf[512];
+ ASSERT_EQ(smoke_len, TEMP_FAILURE_RETRY(read(server_socket, read_buf, sizeof(read_buf))));
+ ASSERT_STREQ(kSmoke, read_buf);
+
+ // Close the server socket.
+ server_socket.reset();
+ ASSERT_EQ(0, unlink(socket_path.c_str())) << strerror(errno);
+
+ // Ensure that write() from the client returns an error since the server is closed.
+ ASSERT_EQ(-1, TEMP_FAILURE_RETRY(write(client_socket, kSmoke, sizeof(kSmoke))));
+ ASSERT_EQ(errno, ECONNREFUSED) << strerror(errno);
+
+ // Open the server socket again.
+ open_server_socket();
+
+ // Reconnect the same client socket.
+ ASSERT_EQ(0,
+ TEMP_FAILURE_RETRY(connect(client_socket, reinterpret_cast<sockaddr*>(&client_sockaddr),
+ sizeof(client_sockaddr))))
+ << strerror(errno);
+
+ // Ensure that communication works.
+ ASSERT_EQ(smoke_len, TEMP_FAILURE_RETRY(write(client_socket, kSmoke, sizeof(kSmoke))));
+ ASSERT_EQ(smoke_len, TEMP_FAILURE_RETRY(read(server_socket, read_buf, sizeof(read_buf))));
+ ASSERT_STREQ(kSmoke, read_buf);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
\ No newline at end of file
diff --git a/liblog/tests/rwlock_test.cpp b/liblog/tests/rwlock_test.cpp
deleted file mode 100644
index 617d5c4..0000000
--- a/liblog/tests/rwlock_test.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * 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 "../rwlock.h"
-
-#include <chrono>
-#include <shared_mutex>
-#include <thread>
-
-#include <gtest/gtest.h>
-
-using namespace std::literals;
-
-TEST(rwlock, reader_then_reader_lock) {
- RwLock lock;
-
- bool thread_ran = false;
- auto read_guard = std::shared_lock{lock};
-
- auto reader_thread = std::thread([&] {
- auto read_guard = std::shared_lock{lock};
- thread_ran = true;
- });
-
- auto end_time = std::chrono::steady_clock::now() + 1s;
-
- while (std::chrono::steady_clock::now() < end_time) {
- if (thread_ran) {
- break;
- }
- }
-
- EXPECT_EQ(true, thread_ran);
-
- // Unlock the lock in case something went wrong, to ensure that we can still join() the thread.
- read_guard.unlock();
- reader_thread.join();
-}
-
-template <template <typename> typename L1, template <typename> typename L2>
-void TestBlockingLocks() {
- RwLock lock;
-
- bool thread_ran = false;
- auto read_guard = L1{lock};
-
- auto reader_thread = std::thread([&] {
- auto read_guard = L2{lock};
- thread_ran = true;
- });
-
- auto end_time = std::chrono::steady_clock::now() + 1s;
-
- while (std::chrono::steady_clock::now() < end_time) {
- if (thread_ran) {
- break;
- }
- }
-
- EXPECT_EQ(false, thread_ran);
-
- read_guard.unlock();
- reader_thread.join();
-
- EXPECT_EQ(true, thread_ran);
-}
-
-TEST(rwlock, reader_then_writer_lock) {
- TestBlockingLocks<std::shared_lock, std::unique_lock>();
-}
-
-TEST(rwlock, writer_then_reader_lock) {
- TestBlockingLocks<std::unique_lock, std::shared_lock>();
-}
-
-TEST(rwlock, writer_then_writer_lock) {
- TestBlockingLocks<std::unique_lock, std::unique_lock>();
-}
diff --git a/libprocessgroup/profiles/Android.bp b/libprocessgroup/profiles/Android.bp
index 12474f1..766ea0f 100644
--- a/libprocessgroup/profiles/Android.bp
+++ b/libprocessgroup/profiles/Android.bp
@@ -100,13 +100,12 @@
"libjsoncpp",
"libprotobuf-cpp-full",
],
- target: {
- android: {
- test_config: "vts_processgroup_validate_test.xml",
- },
- },
+ test_suites: [
+ "vts",
+ ],
}
vts_config {
name: "VtsProcessgroupValidateTest",
+ test_config: "vts_processgroup_validate_test.xml",
}
diff --git a/llkd/libllkd.cpp b/llkd/libllkd.cpp
index 1c3acb8..8ad9900 100644
--- a/llkd/libllkd.cpp
+++ b/llkd/libllkd.cpp
@@ -41,6 +41,7 @@
#include <string>
#include <unordered_map>
#include <unordered_set>
+#include <vector>
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -1204,9 +1205,19 @@
}
}
// We are here because we have confirmed kernel live-lock
+ std::vector<std::string> threads;
+ auto taskdir = procdir + std::to_string(tid) + "/task/";
+ dir taskDirectory(taskdir);
+ for (auto tp = taskDirectory.read(); tp != nullptr; tp = taskDirectory.read()) {
+ std::string piddir;
+ if (getValidTidDir(tp, &piddir))
+ threads.push_back(android::base::Basename(piddir));
+ }
const auto message = state + " "s + llkFormat(procp->count) + " " +
std::to_string(ppid) + "->" + std::to_string(pid) + "->" +
- std::to_string(tid) + " " + process_comm + " [panic]";
+ std::to_string(tid) + " " + process_comm + " [panic]\n" +
+ " thread group: {" + android::base::Join(threads, ",") +
+ "}";
llkPanicKernel(dump, tid,
(state == 'Z') ? "zombie" : (state == 'D') ? "driver" : "sleeping",
message);
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index b065855..8185f01 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -50,6 +50,7 @@
#include <android/log.h>
#include <log/event_tag_map.h>
#include <log/log_id.h>
+#include <log/log_read.h>
#include <log/logprint.h>
#include <private/android_logger.h>
#include <processgroup/sched_policy.h>
@@ -122,6 +123,18 @@
return fd;
}
+static void closeLogFile(const char* pathname) {
+ int fd = open(pathname, O_WRONLY | O_CLOEXEC);
+ if (fd == -1) {
+ return;
+ }
+
+ // no need to check errors
+ __u32 set = 0;
+ ioctl(fd, F2FS_IOC_SET_PIN_FILE, &set);
+ close(fd);
+}
+
void Logcat::RotateLogs() {
// Can't rotate logs if we're not outputting to a file
if (!output_file_name_) return;
@@ -152,6 +165,8 @@
break;
}
+ closeLogFile(file0.c_str());
+
int err = rename(file0.c_str(), file1.c_str());
if (err < 0 && errno != ENOENT) {
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 3714800..916ed42 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -22,6 +22,7 @@
#include <time.h>
#include <unistd.h>
+#include <log/log_read.h>
#include <private/android_logger.h>
#include "LogBuffer.h"
diff --git a/logd/LogTags.cpp b/logd/LogTags.cpp
index 8299e66..e45cc8a 100644
--- a/logd/LogTags.cpp
+++ b/logd/LogTags.cpp
@@ -34,6 +34,7 @@
#include <android-base/stringprintf.h>
#include <log/log_event_list.h>
#include <log/log_properties.h>
+#include <log/log_read.h>
#include <private/android_filesystem_config.h>
#include "LogTags.h"
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index d57b79e..b092489 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -32,6 +32,7 @@
#include <android-base/stringprintf.h>
#include <cutils/sockets.h>
#include <gtest/gtest.h>
+#include <log/log_read.h>
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
#ifdef __ANDROID__
diff --git a/toolbox/start.cpp b/toolbox/start.cpp
index b87ed15..46314cf 100644
--- a/toolbox/start.cpp
+++ b/toolbox/start.cpp
@@ -36,7 +36,12 @@
}
static void ControlDefaultServices(bool start) {
- std::vector<std::string> services = {"netd", "surfaceflinger", "zygote"};
+ std::vector<std::string> services = {
+ "netd",
+ "surfaceflinger",
+ "audioserver",
+ "zygote",
+ };
// Only start zygote_secondary if not single arch.
std::string zygote_configuration = GetProperty("ro.zygote", "");
@@ -86,4 +91,4 @@
extern "C" int stop_main(int argc, char** argv) {
return StartStop(argc, argv, false);
-}
\ No newline at end of file
+}