Merge "Set block size in dm-bow"
diff --git a/CleanSpec.mk b/CleanSpec.mk
index c84bd24..0a534a2 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -90,3 +90,4 @@
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/debug_ramdisk/product_services)
$(call add-clean-step, find $(PRODUCT_OUT) -type l -name "charger" -print0 | xargs -0 rm -f)
$(call add-clean-step, rm -f $(PRODUCT_OUT)/system/bin/adbd)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/init/snapshotctl.rc)
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..d97975c
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,3 @@
+third_party {
+ license_type: NOTICE
+}
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/TEST_MAPPING b/TEST_MAPPING
index e3a8675..7933629 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -28,9 +28,6 @@
"name": "fs_mgr_vendor_overlay_test"
},
{
- "name": "libbase_test"
- },
- {
"name": "libpackagelistparser_test"
},
{
@@ -60,9 +57,6 @@
},
{
"name": "propertyinfoserializer_tests"
- },
- {
- "name": "ziparchive-tests"
}
]
}
diff --git a/adb/Android.bp b/adb/Android.bp
index a26017f..8747182 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -114,6 +114,47 @@
},
}
+cc_defaults {
+ name: "libadbd_binary_dependencies",
+ static_libs: [
+ "libadb_crypto",
+ "libadb_pairing_connection",
+ "libadb_tls_connection",
+ "libadbd",
+ "libadbd_core",
+ "libadbconnection_server",
+ "libasyncio",
+ "libbase",
+ "libbrotli",
+ "libcutils_sockets",
+ "libdiagnose_usb",
+ "libmdnssd",
+ "libzstd",
+
+ "libadb_protos",
+ "libapp_processes_protos_lite",
+ "libprotobuf-cpp-lite",
+ ],
+
+ shared_libs: [
+ "libadbd_auth",
+ "libadbd_fs",
+ "libcrypto",
+ "libcrypto_utils",
+ "liblog",
+ "libselinux",
+ ],
+
+ target: {
+ recovery: {
+ exclude_static_libs: [
+ "libadb_pairing_auth",
+ "libadb_pairing_connection",
+ ],
+ },
+ },
+}
+
// libadb
// =========================================================
// These files are compiled for both the host and the device.
@@ -125,18 +166,26 @@
"adb_unique_fd.cpp",
"adb_utils.cpp",
"fdevent/fdevent.cpp",
- "fdevent/fdevent_poll.cpp",
"services.cpp",
"sockets.cpp",
"socket_spec.cpp",
"sysdeps/errno.cpp",
"transport.cpp",
"transport_fd.cpp",
- "transport_local.cpp",
- "transport_usb.cpp",
"types.cpp",
]
+libadb_darwin_srcs = [
+ "fdevent/fdevent_poll.cpp",
+]
+
+libadb_windows_srcs = [
+ "fdevent/fdevent_poll.cpp",
+ "sysdeps_win32.cpp",
+ "sysdeps/win32/errno.cpp",
+ "sysdeps/win32/stat.cpp",
+]
+
libadb_posix_srcs = [
"sysdeps_unix.cpp",
"sysdeps/posix/network.cpp",
@@ -168,7 +217,10 @@
"client/adb_wifi.cpp",
"client/usb_libusb.cpp",
"client/usb_dispatch.cpp",
+ "client/transport_local.cpp",
"client/transport_mdns.cpp",
+ "client/mdns_utils.cpp",
+ "client/transport_usb.cpp",
"client/pairing/pairing_client.cpp",
],
@@ -179,7 +231,7 @@
srcs: ["client/usb_linux.cpp"] + libadb_linux_srcs,
},
darwin: {
- srcs: ["client/usb_osx.cpp"],
+ srcs: ["client/usb_osx.cpp"] + libadb_darwin_srcs,
},
not_windows: {
srcs: libadb_posix_srcs,
@@ -188,10 +240,7 @@
enabled: true,
srcs: [
"client/usb_windows.cpp",
- "sysdeps_win32.cpp",
- "sysdeps/win32/errno.cpp",
- "sysdeps/win32/stat.cpp",
- ],
+ ] + libadb_windows_srcs,
shared_libs: ["AdbWinApi"],
},
},
@@ -217,7 +266,10 @@
cc_test_host {
name: "adb_test",
defaults: ["adb_defaults"],
- srcs: libadb_test_srcs,
+ srcs: libadb_test_srcs + [
+ "client/mdns_utils_test.cpp",
+ ],
+
static_libs: [
"libadb_crypto_static",
"libadb_host",
@@ -246,39 +298,6 @@
},
}
-cc_benchmark {
- name: "adb_benchmark",
- defaults: ["adb_defaults"],
-
- srcs: ["transport_benchmark.cpp"],
- target: {
- android: {
- static_libs: [
- "libadbd",
- ],
- },
- host: {
- static_libs: [
- "libadb_host",
- ],
- },
- },
-
- static_libs: [
- "libadb_crypto_static",
- "libadb_tls_connection_static",
- "libadbd_auth",
- "libbase",
- "libcutils",
- "libcrypto_utils",
- "libcrypto_static",
- "libdiagnose_usb",
- "liblog",
- "libssl",
- "libusb",
- ],
-}
-
cc_binary_host {
name: "adb",
@@ -298,6 +317,7 @@
"client/fastdeploycallbacks.cpp",
"client/incremental.cpp",
"client/incremental_server.cpp",
+ "client/incremental_utils.cpp",
"shell_service_protocol.cpp",
],
@@ -309,12 +329,14 @@
static_libs: [
"libadb_crypto",
"libadb_host",
- "libadb_pairing_auth",
- "libadb_pairing_connection",
+ "libadb_pairing_auth",
+ "libadb_pairing_connection",
"libadb_protos",
"libadb_tls_connection",
"libandroidfw",
+ "libapp_processes_protos_full",
"libbase",
+ "libbrotli",
"libcutils",
"libcrypto_utils",
"libcrypto",
@@ -323,13 +345,14 @@
"liblog",
"liblz4",
"libmdnssd",
- "libprotobuf-cpp-lite",
+ "libprotobuf-cpp-full",
"libssl",
"libusb",
"libutils",
"liblog",
"libziparchive",
"libz",
+ "libzstd",
],
// Don't add anything here, we don't want additional shared dependencies
@@ -373,36 +396,41 @@
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",
- ],
-
- local_include_dirs: [
- "daemon/include",
+ "daemon/transport_local.cpp",
],
generated_headers: ["platform_tools_version"],
static_libs: [
- "libadbconnection_server",
"libdiagnose_usb",
],
shared_libs: [
+ "libadbconnection_server",
"libadb_crypto",
"libadb_pairing_connection",
"libadb_protos",
"libadb_tls_connection",
"libadbd_auth",
+ "libapp_processes_protos_lite",
"libasyncio",
"libbase",
"libcrypto",
"libcrypto_utils",
+ "libcutils_sockets",
"liblog",
],
+ proto: {
+ type: "lite",
+ static: true,
+ export_proto_headers: true,
+ },
+
target: {
android: {
whole_static_libs: [
@@ -412,24 +440,28 @@
"daemon/transport_qemu.cpp",
"daemon/usb.cpp",
"daemon/usb_ffs.cpp",
- "daemon/usb_legacy.cpp",
- ]
- },
- linux_glibc: {
- srcs: [
- "daemon/usb_dummy.cpp",
]
},
recovery: {
exclude_shared_libs: [
"libadb_pairing_auth",
"libadb_pairing_connection",
+ "libapp_processes_protos_lite",
],
}
},
+
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.adbd",
+ ],
+ visibility: [
+ "//bootable/recovery/minadbd",
+ "//system/core/adb",
+ ],
}
-cc_library_static {
+cc_library {
name: "libadbd_services",
defaults: ["adbd_defaults", "host_adbd_supported"],
recovery_available: true,
@@ -450,7 +482,10 @@
static_libs: [
"libadbconnection_server",
"libadbd_core",
+ "libbrotli",
"libdiagnose_usb",
+ "liblz4",
+ "libzstd",
],
shared_libs: [
@@ -458,12 +493,17 @@
"libadb_pairing_connection",
"libadb_protos",
"libadb_tls_connection",
- "libadbd_auth",
- "libadbd_fs",
+ "libapp_processes_protos_lite",
"libasyncio",
"libbase",
- "libcrypto",
"libcrypto_utils",
+ "libcutils_sockets",
+ "libprotobuf-cpp-lite",
+
+ // APEX dependencies.
+ "libadbd_auth",
+ "libadbd_fs",
+ "libcrypto",
"liblog",
],
@@ -490,12 +530,22 @@
],
},
},
+
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.adbd",
+ ],
+ visibility: [
+ "//system/core/adb",
+ ],
+
}
cc_library {
name: "libadbd",
defaults: ["adbd_defaults", "host_adbd_supported"],
recovery_available: true,
+ apex_available: ["com.android.adbd"],
// avoid getting duplicate symbol of android::build::getbuildnumber().
use_version_lib: false,
@@ -503,24 +553,24 @@
// libminadbd wants both, as it's used to build native tests.
compile_multilib: "both",
- // libadbd doesn't build any additional source, but to expose libadbd_core as a shared library.
- whole_static_libs: [
- "libadbconnection_server",
- "libadbd_core",
- ],
-
shared_libs: [
+ "libadbconnection_server",
+ "libapp_processes_protos_lite",
+ "libprotobuf-cpp-lite",
"libadb_crypto",
"libadb_pairing_connection",
"libadb_tls_connection",
- "libadbd_auth",
- "libadbd_fs",
"libasyncio",
"libbase",
"libcrypto",
"libcrypto_utils",
"liblog",
"libselinux",
+
+ // APEX dependencies on the system image.
+ "libadbd_auth",
+ "libadbd_fs",
+ "libadbd_services",
],
target: {
@@ -533,22 +583,26 @@
},
static_libs: [
- "libadbd_services",
+ "libadbd_core",
+ "libbrotli",
"libcutils_sockets",
"libdiagnose_usb",
+ "liblz4",
"libmdnssd",
+ "libzstd",
],
- export_include_dirs: [
- "daemon/include",
+ visibility: [
+ "//bootable/recovery/minadbd",
+ "//system/core/adb",
],
}
cc_binary {
name: "adbd",
- defaults: ["adbd_defaults", "host_adbd_supported"],
- stl: "libc++_static",
+ defaults: ["adbd_defaults", "host_adbd_supported", "libadbd_binary_dependencies"],
recovery_available: true,
+ apex_available: ["com.android.adbd"],
srcs: [
"daemon/main.cpp",
@@ -564,30 +618,18 @@
},
static_libs: [
- "libadb_crypto",
- "libadb_tls_connection",
- "libadbconnection_server",
"libadbd",
"libadbd_services",
"libasyncio",
- "libbase",
"libcap",
- "libcrypto_utils",
- "libcutils_sockets",
- "libdiagnose_usb",
- "liblog",
- "libmdnssd",
+ "liblz4",
"libminijail",
- "libselinux",
"libssl",
],
shared_libs: [
- "libadb_pairing_connection",
"libadb_protos",
"libadbd_auth",
- "libadbd_fs",
- "libcrypto",
],
target: {
@@ -659,8 +701,7 @@
cc_test {
name: "adbd_test",
- defaults: ["adbd_defaults"],
- stl: "libc++_static",
+ defaults: ["adbd_defaults", "libadbd_binary_dependencies"],
recovery_available: false,
srcs: libadb_test_srcs + [
@@ -669,23 +710,19 @@
"daemon/shell_service_test.cpp",
"shell_service_protocol.cpp",
"shell_service_protocol_test.cpp",
+ "mdns_test.cpp",
+ ],
+
+ shared_libs: [
+ "liblog",
],
static_libs: [
"libadbd",
"libadbd_auth",
- "libadb_crypto_static",
- "libadb_pairing_connection_static",
- "libadb_tls_connection_static",
"libbase",
"libcrypto_utils",
- "libcrypto_static",
- "libcutils_sockets",
- "libdiagnose_usb",
- "liblog",
"libusb",
- "libmdnssd",
- "libselinux",
],
test_suites: ["device-tests", "mts"],
require_root: true,
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 98db191..08986b7 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -66,6 +66,10 @@
#include "daemon/logging.h"
#endif
+#if ADB_HOST
+#include "client/usb.h"
+#endif
+
std::string adb_version() {
// Don't change the format of this --- it's parsed by ddmlib.
return android::base::StringPrintf(
@@ -105,7 +109,9 @@
{
D("adb: online");
t->online = 1;
+#if ADB_HOST
t->SetConnectionEstablished(true);
+#endif
}
void handle_offline(atransport *t)
@@ -145,7 +151,7 @@
case A_WRTE: tag = "WRTE"; break;
case A_AUTH: tag = "AUTH"; break;
case A_STLS:
- tag = "ATLS";
+ tag = "STLS";
break;
default: tag = "????"; break;
}
@@ -1016,8 +1022,12 @@
if (kill_forward) {
r = remove_listener(pieces[0].c_str(), transport);
} else {
- r = install_listener(pieces[0], pieces[1].c_str(), transport, no_rebind,
- &resolved_tcp_port, &error);
+ int flags = 0;
+ if (no_rebind) {
+ flags |= INSTALL_LISTENER_NO_REBIND;
+ }
+ r = install_listener(pieces[0], pieces[1].c_str(), transport, flags, &resolved_tcp_port,
+ &error);
}
if (r == INSTALL_STATUS_OK) {
#if ADB_HOST
@@ -1063,19 +1073,44 @@
return 0;
}
+static bool g_reject_kill_server = false;
+void adb_set_reject_kill_server(bool value) {
+ g_reject_kill_server = value;
+}
+
+static bool handle_mdns_request(std::string_view service, int reply_fd) {
+ if (!android::base::ConsumePrefix(&service, "mdns:")) {
+ return false;
+ }
+
+ if (service == "check") {
+ std::string check = mdns_check();
+ SendOkay(reply_fd, check);
+ return true;
+ }
+ if (service == "services") {
+ std::string services_list = mdns_list_discovered_services();
+ SendOkay(reply_fd, services_list);
+ return true;
+ }
+
+ return false;
+}
+
HostRequestResult handle_host_request(std::string_view service, TransportType type,
const char* serial, TransportId transport_id, int reply_fd,
asocket* s) {
if (service == "kill") {
- fprintf(stderr, "adb server killed by remote request\n");
- fflush(stdout);
+ if (g_reject_kill_server) {
+ LOG(WARNING) << "adb server ignoring kill-server";
+ SendFail(reply_fd, "kill-server rejected by remote server");
+ } else {
+ fprintf(stderr, "adb server killed by remote request\n");
+ SendOkay(reply_fd);
- // Send a reply even though we don't read it anymore, so that old versions
- // of adb that do read it don't spew error messages.
- SendOkay(reply_fd);
-
- // Rely on process exit to close the socket for us.
- exit(0);
+ // Rely on process exit to close the socket for us.
+ exit(0);
+ }
}
LOG(DEBUG) << "handle_host_request(" << service << ")";
@@ -1184,9 +1219,9 @@
FeatureSet features = supported_features();
// Abuse features to report libusb status.
if (should_use_libusb()) {
- features.insert(kFeatureLibusb);
+ features.emplace_back(kFeatureLibusb);
}
- features.insert(kFeaturePushSync);
+ features.emplace_back(kFeaturePushSync);
SendOkay(reply_fd, FeatureSetToString(features));
return HostRequestResult::Handled;
}
@@ -1306,6 +1341,10 @@
return HostRequestResult::Handled;
}
+ if (handle_mdns_request(service, reply_fd)) {
+ return HostRequestResult::Handled;
+ }
+
return HostRequestResult::Unhandled;
}
diff --git a/adb/adb.h b/adb/adb.h
index 86d205c..476ed9b 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef __ADB_H
-#define __ADB_H
+#pragma once
#include <limits.h>
#include <stdint.h>
@@ -29,7 +28,6 @@
#include "fdevent/fdevent.h"
#include "socket.h"
#include "types.h"
-#include "usb.h"
constexpr size_t MAX_PAYLOAD_V1 = 4 * 1024;
constexpr size_t MAX_PAYLOAD = 1024 * 1024;
@@ -139,7 +137,6 @@
/* initialize a transport object's func pointers and state */
int init_socket_transport(atransport* t, unique_fd s, int port, int local);
-void init_usb_transport(atransport* t, usb_handle* usb);
std::string getEmulatorSerialString(int console_port);
#if ADB_HOST
@@ -169,6 +166,7 @@
int init_jdwp(void);
asocket* create_jdwp_service_socket();
asocket* create_jdwp_tracker_service_socket();
+asocket* create_app_tracker_service_socket();
unique_fd create_jdwp_connection_fd(int jdwp_pid);
#endif
@@ -238,6 +236,7 @@
void parse_banner(const std::string&, atransport* t);
+#if ADB_HOST
// On startup, the adb server needs to wait until all of the connected devices are ready.
// To do this, we need to know when the scan has identified all of the potential new transports, and
// when each transport becomes ready.
@@ -251,5 +250,12 @@
// Wait until device scan has completed and every transport is ready, or a timeout elapses.
void adb_wait_for_device_initialization();
+#endif // ADB_HOST
+#if ADB_HOST
+// When ssh-forwarding to a remote adb server, kill-server is almost never what you actually want,
+// and unfortunately, many other tools issue it. This adds a knob to reject kill-servers.
+void adb_set_reject_kill_server(bool reject);
#endif
+
+void usb_init();
diff --git a/adb/adb_listeners.cpp b/adb/adb_listeners.cpp
index 29909a5..124e2d8 100644
--- a/adb/adb_listeners.cpp
+++ b/adb/adb_listeners.cpp
@@ -73,6 +73,7 @@
typedef std::list<std::unique_ptr<alistener>> ListenerList;
static ListenerList& listener_list GUARDED_BY(listener_list_mutex) = *new ListenerList();
+#if ADB_HOST
static void ss_listener_event_func(int _fd, unsigned ev, void *_l) {
if (ev & FDE_READ) {
unique_fd fd(adb_socket_accept(_fd, nullptr, nullptr));
@@ -88,6 +89,7 @@
}
}
}
+#endif
static void listener_event_func(int _fd, unsigned ev, void* _l)
{
@@ -164,6 +166,16 @@
}
}
+void enable_server_sockets() EXCLUDES(listener_list_mutex) {
+ std::lock_guard<std::mutex> lock(listener_list_mutex);
+ for (auto& l : listener_list) {
+ if (l->connect_to == "*smartsocket*") {
+ fdevent_set(l->fde, FDE_READ);
+ }
+ }
+}
+
+#if ADB_HOST
void close_smartsockets() EXCLUDES(listener_list_mutex) {
std::lock_guard<std::mutex> lock(listener_list_mutex);
auto pred = [](const std::unique_ptr<alistener>& listener) {
@@ -171,21 +183,22 @@
};
listener_list.remove_if(pred);
}
+#endif
InstallStatus install_listener(const std::string& local_name, const char* connect_to,
- atransport* transport, int no_rebind, int* resolved_tcp_port,
+ atransport* transport, int flags, int* resolved_tcp_port,
std::string* error) EXCLUDES(listener_list_mutex) {
std::lock_guard<std::mutex> lock(listener_list_mutex);
for (auto& l : listener_list) {
if (local_name == l->local_name) {
// Can't repurpose a smartsocket.
- if(l->connect_to[0] == '*') {
+ if (l->connect_to[0] == '*') {
*error = "cannot repurpose smartsocket";
return INSTALL_STATUS_INTERNAL_ERROR;
}
- // Can't repurpose a listener if 'no_rebind' is true.
- if (no_rebind) {
+ // Can't repurpose a listener if INSTALL_LISTENER_NO_REBIND is set
+ if (flags & INSTALL_LISTENER_NO_REBIND) {
*error = "cannot rebind";
return INSTALL_STATUS_CANNOT_REBIND;
}
@@ -218,11 +231,17 @@
close_on_exec(listener->fd);
if (listener->connect_to == "*smartsocket*") {
+#if ADB_HOST
listener->fde = fdevent_create(listener->fd, ss_listener_event_func, listener.get());
+#else
+ LOG(FATAL) << "attempted to connect to *smartsocket* in daemon";
+#endif
} else {
listener->fde = fdevent_create(listener->fd, listener_event_func, listener.get());
}
- fdevent_set(listener->fde, FDE_READ);
+ if ((flags & INSTALL_LISTENER_DISABLED) == 0) {
+ fdevent_set(listener->fde, FDE_READ);
+ }
listener->transport = transport;
diff --git a/adb/adb_listeners.h b/adb/adb_listeners.h
index 70a2ee1..0aa774a 100644
--- a/adb/adb_listeners.h
+++ b/adb/adb_listeners.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef __ADB_LISTENERS_H
-#define __ADB_LISTENERS_H
+#pragma once
#include "adb.h"
@@ -32,8 +31,11 @@
INSTALL_STATUS_LISTENER_NOT_FOUND = -4,
};
+inline constexpr int INSTALL_LISTENER_NO_REBIND = 1 << 0;
+inline constexpr int INSTALL_LISTENER_DISABLED = 1 << 1;
+
InstallStatus install_listener(const std::string& local_name, const char* connect_to,
- atransport* transport, int no_rebind, int* resolved_tcp_port,
+ atransport* transport, int flags, int* resolved_tcp_port,
std::string* error);
std::string format_listeners();
@@ -41,6 +43,7 @@
InstallStatus remove_listener(const char* local_name, atransport* transport);
void remove_all_listeners(void);
+#if ADB_HOST
+void enable_server_sockets();
void close_smartsockets();
-
-#endif /* __ADB_LISTENERS_H */
+#endif
diff --git a/adb/adb_mdns.h b/adb/adb_mdns.h
index 6b37355..3111248 100644
--- a/adb/adb_mdns.h
+++ b/adb/adb_mdns.h
@@ -19,9 +19,14 @@
#include <android-base/macros.h>
-const char* kADBServiceType = "_adb._tcp";
-const char* kADBSecurePairingServiceType = "_adb_secure_pairing._tcp";
-const char* kADBSecureConnectServiceType = "_adb_secure_connect._tcp";
+// The rules for Service Names [RFC6335] state that they may be no more
+// than fifteen characters long (not counting the mandatory underscore),
+// consisting of only letters, digits, and hyphens, must begin and end
+// with a letter or digit, must not contain consecutive hyphens, and
+// must contain at least one letter.
+#define ADB_MDNS_SERVICE_TYPE "adb"
+#define ADB_MDNS_TLS_PAIRING_TYPE "adb-tls-pairing"
+#define ADB_MDNS_TLS_CONNECT_TYPE "adb-tls-connect"
const int kADBTransportServiceRefIndex = 0;
const int kADBSecurePairingServiceRefIndex = 1;
@@ -71,11 +76,10 @@
const char* kADBSecureConnectServiceTxtRecord =
ADB_SECURE_SERVICE_VERSION_TXT_RECORD(ADB_SECURE_SERVICE_VERSION);
-const char* kADBDNSServices[] = {
- kADBServiceType,
- kADBSecurePairingServiceType,
- kADBSecureConnectServiceType,
-};
+#define ADB_FULL_MDNS_SERVICE_TYPE(atype) ("_" atype "._tcp")
+const char* kADBDNSServices[] = {ADB_FULL_MDNS_SERVICE_TYPE(ADB_MDNS_SERVICE_TYPE),
+ ADB_FULL_MDNS_SERVICE_TYPE(ADB_MDNS_TLS_PAIRING_TYPE),
+ ADB_FULL_MDNS_SERVICE_TYPE(ADB_MDNS_TLS_CONNECT_TYPE)};
const char* kADBDNSServiceTxtRecords[] = {
nullptr,
diff --git a/adb/adb_trace.cpp b/adb/adb_trace.cpp
index cea24fe..210241c 100644
--- a/adb/adb_trace.cpp
+++ b/adb/adb_trace.cpp
@@ -89,18 +89,13 @@
int adb_trace_mask;
-std::string get_trace_setting_from_env() {
+std::string get_trace_setting() {
+#if ADB_HOST || !defined(__ANDROID__)
const char* setting = getenv("ADB_TRACE");
if (setting == nullptr) {
setting = "";
}
-
- return std::string(setting);
-}
-
-std::string get_trace_setting() {
-#if ADB_HOST
- return get_trace_setting_from_env();
+ return setting;
#else
return android::base::GetProperty("persist.adb.trace_mask", "");
#endif
diff --git a/adb/adb_wifi.h b/adb/adb_wifi.h
index 585748c..42f414b 100644
--- a/adb/adb_wifi.h
+++ b/adb/adb_wifi.h
@@ -16,6 +16,7 @@
#pragma once
+#include <optional>
#include <string>
#include "adb.h"
@@ -27,6 +28,22 @@
std::string& response);
bool adb_wifi_is_known_host(const std::string& host);
+std::string mdns_check();
+std::string mdns_list_discovered_services();
+
+struct MdnsInfo {
+ std::string service_name;
+ std::string service_type;
+ std::string addr;
+ uint16_t port = 0;
+
+ MdnsInfo(std::string_view name, std::string_view type, std::string_view addr, uint16_t port)
+ : service_name(name), service_type(type), addr(addr), port(port) {}
+};
+
+std::optional<MdnsInfo> mdns_get_connect_service_info(std::string_view name);
+std::optional<MdnsInfo> mdns_get_pairing_service_info(std::string_view name);
+
#else // !ADB_HOST
struct AdbdAuthContext;
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_client.cpp b/adb/client/adb_client.cpp
index f724cb5..a308732 100644
--- a/adb/client/adb_client.cpp
+++ b/adb/client/adb_client.cpp
@@ -37,6 +37,7 @@
#include <vector>
#include <android-base/file.h>
+#include <android-base/no_destructor.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/thread_annotations.h>
@@ -204,9 +205,25 @@
return false;
}
- // The server might send OKAY, so consume that.
char buf[4];
- ReadFdExactly(fd.get(), buf, 4);
+ if (!ReadFdExactly(fd.get(), buf, 4)) {
+ fprintf(stderr, "error: failed to read response from server\n");
+ return false;
+ }
+
+ if (memcmp(buf, "OKAY", 4) == 0) {
+ // Nothing to do.
+ } else if (memcmp(buf, "FAIL", 4) == 0) {
+ std::string output, error;
+ if (!ReadProtocolString(fd.get(), &output, &error)) {
+ fprintf(stderr, "error: %s\n", error.c_str());
+ return false;
+ }
+
+ fprintf(stderr, "error: %s\n", output.c_str());
+ return false;
+ }
+
// Now that no more data is expected, wait for socket orderly shutdown or error, indicating
// server death.
ReadOrderlyShutdown(fd.get());
@@ -399,18 +416,20 @@
return android::base::StringPrintf("%s:%s", prefix, command);
}
-bool adb_get_feature_set(FeatureSet* feature_set, std::string* error) {
- static FeatureSet* features = nullptr;
+const std::optional<FeatureSet>& adb_get_feature_set(std::string* error) {
+ static std::mutex feature_mutex [[clang::no_destroy]];
+ static std::optional<FeatureSet> features [[clang::no_destroy]] GUARDED_BY(feature_mutex);
+ std::lock_guard<std::mutex> lock(feature_mutex);
if (!features) {
std::string result;
- if (adb_query(format_host_command("features"), &result, error)) {
- features = new FeatureSet(StringToFeatureSet(result));
+ std::string err;
+ if (adb_query(format_host_command("features"), &result, &err)) {
+ features = StringToFeatureSet(result);
+ } else {
+ if (error) {
+ *error = err;
+ }
}
}
- if (features) {
- *feature_set = *features;
- return true;
- }
- feature_set->clear();
- return false;
+ return features;
}
diff --git a/adb/client/adb_client.h b/adb/client/adb_client.h
index 1c6cde7..caf4e86 100644
--- a/adb/client/adb_client.h
+++ b/adb/client/adb_client.h
@@ -76,7 +76,7 @@
std::string format_host_command(const char* _Nonnull command);
// Get the feature set of the current preferred transport.
-bool adb_get_feature_set(FeatureSet* _Nonnull feature_set, std::string* _Nonnull error);
+const std::optional<FeatureSet>& adb_get_feature_set(std::string* _Nullable error);
#if defined(__linux__)
// Get the path of a file containing the path to the server executable, if the socket spec set via
@@ -90,8 +90,9 @@
// ADB Secure DNS service interface. Used to query what ADB Secure DNS services have been
// resolved, and to run some kind of callback for each one.
-using adb_secure_foreach_service_callback = std::function<void(
- const char* _Nonnull service_name, const char* _Nonnull ip_address, uint16_t port)>;
+using adb_secure_foreach_service_callback =
+ std::function<void(const char* _Nonnull service_name, const char* _Nonnull reg_type,
+ const char* _Nonnull ip_address, uint16_t port)>;
// Queries pairing/connect services that have been discovered and resolved.
// If |host_name| is not null, run |cb| only for services
diff --git a/adb/client/adb_install.cpp b/adb/client/adb_install.cpp
index 21b8f49..d6f536e 100644
--- a/adb/client/adb_install.cpp
+++ b/adb/client/adb_install.cpp
@@ -22,11 +22,12 @@
#include <stdlib.h>
#include <unistd.h>
#include <algorithm>
-#include <iostream>
#include <string>
+#include <string_view>
#include <vector>
#include <android-base/file.h>
+#include <android-base/parsebool.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
@@ -39,8 +40,9 @@
#include "fastdeploy.h"
#include "incremental.h"
+using namespace std::literals;
+
static constexpr int kFastDeployMinApi = 24;
-static constexpr int kIncrementalMinApi = 29;
namespace {
@@ -50,16 +52,17 @@
INSTALL_STREAM,
INSTALL_INCREMENTAL,
};
+
+enum class CmdlineOption { None, Enable, Disable };
}
static bool can_use_feature(const char* feature) {
- FeatureSet features;
- std::string error;
- if (!adb_get_feature_set(&features, &error)) {
- fprintf(stderr, "error: %s\n", error.c_str());
+ // We ignore errors here, if the device is missing, we'll notice when we try to push install.
+ auto&& features = adb_get_feature_set(nullptr);
+ if (!features) {
return false;
}
- return CanUseFeature(features, feature);
+ return CanUseFeature(*features, feature);
}
static InstallMode best_install_mode() {
@@ -151,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");
@@ -223,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;
@@ -286,7 +292,7 @@
}
}
- if (do_sync_push(apk_file, apk_dest.c_str(), false)) {
+ if (do_sync_push(apk_file, apk_dest.c_str(), false, CompressionType::Any, false)) {
result = pm_command(argc, argv);
delete_device_file(apk_dest);
}
@@ -295,49 +301,55 @@
}
template <class TimePoint>
-static int msBetween(TimePoint start, TimePoint end) {
+static int ms_between(TimePoint start, TimePoint end) {
return std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
}
-static int install_app_incremental(int argc, const char** argv) {
- printf("Performing Incremental Install\n");
+static int install_app_incremental(int argc, const char** argv, bool wait, bool silent) {
using clock = std::chrono::high_resolution_clock;
const auto start = clock::now();
int first_apk = -1;
int last_apk = -1;
- std::string cert_path;
- bool wait = false;
- std::vector<std::string_view> args = {"package"};
+ incremental::Args passthrough_args = {};
for (int i = 0; i < argc; ++i) {
const auto arg = std::string_view(argv[i]);
- if (android::base::EndsWithIgnoreCase(arg, ".apk")) {
+ if (android::base::EndsWithIgnoreCase(arg, ".apk"sv)) {
last_apk = i;
if (first_apk == -1) {
first_apk = i;
}
- } else if (arg == "--wait") {
- wait = true;
- } else if (arg.starts_with("install-")) {
+ } else if (arg.starts_with("install"sv)) {
// incremental installation command on the device is the same for all its variations in
// the adb, e.g. install-multiple or install-multi-package
- args.push_back("install");
} else {
- args.push_back(arg);
+ passthrough_args.push_back(arg);
}
}
- if (first_apk == -1) error_exit("Need at least one APK file on command line");
+ if (first_apk == -1) {
+ if (!silent) {
+ fprintf(stderr, "error: need at least one APK file on command line\n");
+ }
+ return -1;
+ }
- const auto afterApk = clock::now();
+ auto files = incremental::Files{argv + first_apk, argv + last_apk + 1};
+ if (silent) {
+ // For a silent installation we want to do the lightweight check first and bail early and
+ // quietly if it fails.
+ if (!incremental::can_install(files)) {
+ return -1;
+ }
+ }
- auto server_process = incremental::install({argv + first_apk, argv + last_apk + 1});
+ printf("Performing Incremental Install\n");
+ auto server_process = incremental::install(files, passthrough_args, silent);
if (!server_process) {
return -1;
}
const auto end = clock::now();
- printf("Install command complete (ms: %d total, %d apk prep, %d install)\n",
- msBetween(start, end), msBetween(start, afterApk), msBetween(afterApk, end));
+ printf("Install command complete in %d ms\n", ms_between(start, end));
if (wait) {
(*server_process).wait();
@@ -346,97 +358,184 @@
return 0;
}
+static std::pair<InstallMode, std::optional<InstallMode>> calculate_install_mode(
+ InstallMode modeFromArgs, bool fastdeploy, CmdlineOption incremental_request) {
+ if (incremental_request == CmdlineOption::Enable) {
+ if (fastdeploy) {
+ error_exit(
+ "--incremental and --fast-deploy options are incompatible. "
+ "Please choose one");
+ }
+ }
+
+ if (modeFromArgs != INSTALL_DEFAULT) {
+ if (incremental_request == CmdlineOption::Enable) {
+ error_exit("--incremental is not compatible with other installation modes");
+ }
+ return {modeFromArgs, std::nullopt};
+ }
+
+ if (incremental_request != CmdlineOption::Disable && !is_abb_exec_supported()) {
+ if (incremental_request == CmdlineOption::None) {
+ incremental_request = CmdlineOption::Disable;
+ } else {
+ error_exit("Device doesn't support incremental installations");
+ }
+ }
+ if (incremental_request == CmdlineOption::None) {
+ // check if the host is ok with incremental by default
+ if (const char* incrementalFromEnv = getenv("ADB_INSTALL_DEFAULT_INCREMENTAL")) {
+ using namespace android::base;
+ auto val = ParseBool(incrementalFromEnv);
+ if (val == ParseBoolResult::kFalse) {
+ incremental_request = CmdlineOption::Disable;
+ }
+ }
+ }
+ if (incremental_request == CmdlineOption::None) {
+ // still ok: let's see if the device allows using incremental by default
+ // it starts feeling like we're looking for an excuse to not to use incremental...
+ std::string error;
+ std::vector<std::string> args = {"settings", "get",
+ "enable_adb_incremental_install_default"};
+ auto fd = send_abb_exec_command(args, &error);
+ if (!fd.ok()) {
+ fprintf(stderr, "adb: retrieving the default device installation mode failed: %s",
+ error.c_str());
+ } else {
+ char buf[BUFSIZ] = {};
+ read_status_line(fd.get(), buf, sizeof(buf));
+ using namespace android::base;
+ auto val = ParseBool(buf);
+ if (val == ParseBoolResult::kFalse) {
+ incremental_request = CmdlineOption::Disable;
+ }
+ }
+ }
+
+ if (incremental_request == CmdlineOption::Enable) {
+ // explicitly requested - no fallback
+ return {INSTALL_INCREMENTAL, std::nullopt};
+ }
+ const auto bestMode = best_install_mode();
+ if (incremental_request == CmdlineOption::None) {
+ // no opinion - use incremental, fallback to regular on a failure.
+ return {INSTALL_INCREMENTAL, bestMode};
+ }
+ // incremental turned off - use the regular best mode without a fallback.
+ return {bestMode, std::nullopt};
+}
+
+static std::vector<const char*> parse_install_mode(std::vector<const char*> argv,
+ InstallMode* install_mode,
+ CmdlineOption* incremental_request,
+ bool* incremental_wait) {
+ *install_mode = INSTALL_DEFAULT;
+ *incremental_request = CmdlineOption::None;
+ *incremental_wait = false;
+
+ std::vector<const char*> passthrough;
+ for (auto&& arg : argv) {
+ if (arg == "--streaming"sv) {
+ *install_mode = INSTALL_STREAM;
+ } else if (arg == "--no-streaming"sv) {
+ *install_mode = INSTALL_PUSH;
+ } else if (strlen(arg) >= "--incr"sv.size() && "--incremental"sv.starts_with(arg)) {
+ *incremental_request = CmdlineOption::Enable;
+ } else if (strlen(arg) >= "--no-incr"sv.size() && "--no-incremental"sv.starts_with(arg)) {
+ *incremental_request = CmdlineOption::Disable;
+ } else if (arg == "--wait"sv) {
+ *incremental_wait = true;
+ } else {
+ passthrough.push_back(arg);
+ }
+ }
+ return passthrough;
+}
+
+static std::vector<const char*> parse_fast_deploy_mode(
+ std::vector<const char*> argv, bool* use_fastdeploy,
+ FastDeploy_AgentUpdateStrategy* agent_update_strategy) {
+ *use_fastdeploy = false;
+ *agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
+
+ std::vector<const char*> passthrough;
+ for (auto&& arg : argv) {
+ if (arg == "--fastdeploy"sv) {
+ *use_fastdeploy = true;
+ } else if (arg == "--no-fastdeploy"sv) {
+ *use_fastdeploy = false;
+ } else if (arg == "--force-agent"sv) {
+ *agent_update_strategy = FastDeploy_AgentUpdateAlways;
+ } else if (arg == "--date-check-agent"sv) {
+ *agent_update_strategy = FastDeploy_AgentUpdateNewerTimeStamp;
+ } else if (arg == "--version-check-agent"sv) {
+ *agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
+ } else {
+ passthrough.push_back(arg);
+ }
+ }
+ return passthrough;
+}
+
int install_app(int argc, const char** argv) {
- std::vector<int> processedArgIndices;
- InstallMode installMode = INSTALL_DEFAULT;
+ InstallMode install_mode = INSTALL_DEFAULT;
+ auto incremental_request = CmdlineOption::None;
+ bool incremental_wait = false;
+
bool use_fastdeploy = false;
- bool is_reinstall = false;
FastDeploy_AgentUpdateStrategy agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
- for (int i = 1; i < argc; i++) {
- if (!strcmp(argv[i], "--streaming")) {
- processedArgIndices.push_back(i);
- installMode = INSTALL_STREAM;
- } else if (!strcmp(argv[i], "--no-streaming")) {
- processedArgIndices.push_back(i);
- installMode = INSTALL_PUSH;
- } else if (!strcmp(argv[i], "-r")) {
- // Note that this argument is not added to processedArgIndices because it
- // must be passed through to pm
- is_reinstall = true;
- } else if (!strcmp(argv[i], "--fastdeploy")) {
- processedArgIndices.push_back(i);
- use_fastdeploy = true;
- } else if (!strcmp(argv[i], "--no-fastdeploy")) {
- processedArgIndices.push_back(i);
- use_fastdeploy = false;
- } else if (!strcmp(argv[i], "--force-agent")) {
- processedArgIndices.push_back(i);
- agent_update_strategy = FastDeploy_AgentUpdateAlways;
- } else if (!strcmp(argv[i], "--date-check-agent")) {
- processedArgIndices.push_back(i);
- agent_update_strategy = FastDeploy_AgentUpdateNewerTimeStamp;
- } else if (!strcmp(argv[i], "--version-check-agent")) {
- processedArgIndices.push_back(i);
- agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
- } else if (!strcmp(argv[i], "--incremental")) {
- processedArgIndices.push_back(i);
- installMode = INSTALL_INCREMENTAL;
- } else if (!strcmp(argv[i], "--no-incremental")) {
- processedArgIndices.push_back(i);
- installMode = INSTALL_DEFAULT;
- }
- }
+ auto unused_argv = parse_install_mode({argv, argv + argc}, &install_mode, &incremental_request,
+ &incremental_wait);
+ auto passthrough_argv =
+ parse_fast_deploy_mode(std::move(unused_argv), &use_fastdeploy, &agent_update_strategy);
- if (installMode == INSTALL_INCREMENTAL) {
- if (get_device_api_level() < kIncrementalMinApi || !is_abb_exec_supported()) {
- error_exit("Attempting to use incremental install on unsupported device");
- }
- }
-
- if (installMode == INSTALL_DEFAULT) {
- installMode = best_install_mode();
- }
-
- if (installMode == INSTALL_STREAM && best_install_mode() == INSTALL_PUSH) {
+ auto [primary_mode, fallback_mode] =
+ calculate_install_mode(install_mode, use_fastdeploy, incremental_request);
+ if ((primary_mode == INSTALL_STREAM ||
+ fallback_mode.value_or(INSTALL_PUSH) == INSTALL_STREAM) &&
+ best_install_mode() == INSTALL_PUSH) {
error_exit("Attempting to use streaming install on unsupported device");
}
if (use_fastdeploy && get_device_api_level() < kFastDeployMinApi) {
- printf("Fast Deploy is only compatible with devices of API version %d or higher, "
- "ignoring.\n",
- kFastDeployMinApi);
+ fprintf(stderr,
+ "Fast Deploy is only compatible with devices of API version %d or higher, "
+ "ignoring.\n",
+ kFastDeployMinApi);
use_fastdeploy = false;
}
fastdeploy_set_agent_update_strategy(agent_update_strategy);
- std::vector<const char*> passthrough_argv;
- for (int i = 0; i < argc; i++) {
- if (std::find(processedArgIndices.begin(), processedArgIndices.end(), i) ==
- processedArgIndices.end()) {
- passthrough_argv.push_back(argv[i]);
- }
- }
if (passthrough_argv.size() < 2) {
error_exit("install requires an apk argument");
}
- switch (installMode) {
- case INSTALL_PUSH:
- return install_app_legacy(passthrough_argv.size(), passthrough_argv.data(),
- use_fastdeploy);
- case INSTALL_STREAM:
- return install_app_streamed(passthrough_argv.size(), passthrough_argv.data(),
- use_fastdeploy);
- case INSTALL_INCREMENTAL:
- return install_app_incremental(passthrough_argv.size(), passthrough_argv.data());
- case INSTALL_DEFAULT:
- default:
- return 1;
+ auto run_install_mode = [&](InstallMode install_mode, bool silent) {
+ switch (install_mode) {
+ case INSTALL_PUSH:
+ return install_app_legacy(passthrough_argv.size(), passthrough_argv.data(),
+ use_fastdeploy);
+ case INSTALL_STREAM:
+ return install_app_streamed(passthrough_argv.size(), passthrough_argv.data(),
+ use_fastdeploy);
+ case INSTALL_INCREMENTAL:
+ return install_app_incremental(passthrough_argv.size(), passthrough_argv.data(),
+ incremental_wait, silent);
+ case INSTALL_DEFAULT:
+ default:
+ error_exit("invalid install mode");
+ }
+ };
+ auto res = run_install_mode(primary_mode, fallback_mode.has_value());
+ if (res && fallback_mode.value_or(primary_mode) != primary_mode) {
+ res = run_install_mode(*fallback_mode, false);
}
+ return res;
}
-int install_multiple_app(int argc, const char** argv) {
+static int install_multiple_app_streamed(int argc, const char** argv) {
// Find all APK arguments starting at end.
// All other arguments passed through verbatim.
int first_apk = -1;
@@ -461,24 +560,27 @@
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();
+ const std::string install_cmd =
+ use_abb_exec ? "package"
+ : best_install_mode() == INSTALL_PUSH ? "exec:pm" : "exec:cmd package";
- std::string cmd = android::base::StringPrintf("%s install-create -S %" PRIu64,
- install_cmd.c_str(), total_size);
+ 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;
@@ -500,6 +602,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;
@@ -512,10 +615,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) {
@@ -525,7 +633,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;
@@ -550,10 +658,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;
@@ -572,6 +683,44 @@
return EXIT_SUCCESS;
}
+int install_multiple_app(int argc, const char** argv) {
+ InstallMode install_mode = INSTALL_DEFAULT;
+ auto incremental_request = CmdlineOption::None;
+ bool incremental_wait = false;
+ bool use_fastdeploy = false;
+
+ auto passthrough_argv = parse_install_mode({argv + 1, argv + argc}, &install_mode,
+ &incremental_request, &incremental_wait);
+
+ auto [primary_mode, fallback_mode] =
+ calculate_install_mode(install_mode, use_fastdeploy, incremental_request);
+ if ((primary_mode == INSTALL_STREAM ||
+ fallback_mode.value_or(INSTALL_PUSH) == INSTALL_STREAM) &&
+ best_install_mode() == INSTALL_PUSH) {
+ error_exit("Attempting to use streaming install on unsupported device");
+ }
+
+ auto run_install_mode = [&](InstallMode install_mode, bool silent) {
+ switch (install_mode) {
+ case INSTALL_PUSH:
+ case INSTALL_STREAM:
+ return install_multiple_app_streamed(passthrough_argv.size(),
+ passthrough_argv.data());
+ case INSTALL_INCREMENTAL:
+ return install_app_incremental(passthrough_argv.size(), passthrough_argv.data(),
+ incremental_wait, silent);
+ case INSTALL_DEFAULT:
+ default:
+ error_exit("invalid install mode");
+ }
+ };
+ auto res = run_install_mode(primary_mode, fallback_mode.has_value());
+ if (res && fallback_mode.value_or(primary_mode) != primary_mode) {
+ res = run_install_mode(*fallback_mode, false);
+ }
+ return res;
+}
+
int install_multi_package(int argc, const char** argv) {
// Find all APK arguments starting at end.
// All other arguments passed through verbatim.
@@ -596,23 +745,31 @@
fprintf(stderr, "adb: multi-package install is not supported on this device\n");
return EXIT_FAILURE;
}
- std::string install_cmd = "exec:cmd package";
- std::string multi_package_cmd =
- android::base::StringPrintf("%s install-create --multi-package", install_cmd.c_str());
+ const bool use_abb_exec = is_abb_exec_supported();
+ const std::string install_cmd = use_abb_exec ? "package" : "exec:cmd package";
+
+ std::vector<std::string> multi_package_cmd_args = {install_cmd, "install-create",
+ "--multi-package"};
+
+ multi_package_cmd_args.reserve(first_package + 4);
for (int i = 1; i < first_package; i++) {
- multi_package_cmd += " " + escape_arg(argv[i]);
+ if (use_abb_exec) {
+ multi_package_cmd_args.push_back(argv[i]);
+ } else {
+ multi_package_cmd_args.push_back(escape_arg(argv[i]));
+ }
}
if (apex_found) {
- multi_package_cmd += " --staged";
+ multi_package_cmd_args.emplace_back("--staged");
}
// Create multi-package install session
std::string error;
char buf[BUFSIZ];
{
- unique_fd fd(adb_connect(multi_package_cmd, &error));
+ unique_fd fd = send_command(multi_package_cmd_args, &error);
if (fd < 0) {
fprintf(stderr, "adb: connect error for create multi-package: %s\n", error.c_str());
return EXIT_FAILURE;
@@ -634,6 +791,7 @@
fputs(buf, stderr);
return EXIT_FAILURE;
}
+ const auto parent_session_id_str = std::to_string(parent_session_id);
fprintf(stdout, "Created parent session ID %d.\n", parent_session_id);
@@ -641,17 +799,30 @@
// Valid session, now create the individual sessions and stream the APKs
int success = EXIT_FAILURE;
- std::string individual_cmd =
- android::base::StringPrintf("%s install-create", install_cmd.c_str());
- std::string all_session_ids = "";
+ std::vector<std::string> individual_cmd_args = {install_cmd, "install-create"};
for (int i = 1; i < first_package; i++) {
- individual_cmd += " " + escape_arg(argv[i]);
+ if (use_abb_exec) {
+ individual_cmd_args.push_back(argv[i]);
+ } else {
+ individual_cmd_args.push_back(escape_arg(argv[i]));
+ }
}
if (apex_found) {
- individual_cmd += " --staged";
+ individual_cmd_args.emplace_back("--staged");
}
- std::string individual_apex_cmd = individual_cmd + " --apex";
- std::string cmd = "";
+
+ std::vector<std::string> individual_apex_cmd_args;
+ if (apex_found) {
+ individual_apex_cmd_args = individual_cmd_args;
+ individual_apex_cmd_args.emplace_back("--apex");
+ }
+
+ std::vector<std::string> add_session_cmd_args = {
+ install_cmd,
+ "install-add-session",
+ parent_session_id_str,
+ };
+
for (int i = first_package; i < argc; i++) {
const char* file = argv[i];
char buf[BUFSIZ];
@@ -659,9 +830,9 @@
unique_fd fd;
// Create individual install session
if (android::base::EndsWithIgnoreCase(file, ".apex")) {
- fd.reset(adb_connect(individual_apex_cmd, &error));
+ fd = send_command(individual_apex_cmd_args, &error);
} else {
- fd.reset(adb_connect(individual_cmd, &error));
+ fd = send_command(individual_cmd_args, &error);
}
if (fd < 0) {
fprintf(stderr, "adb: connect error for create: %s\n", error.c_str());
@@ -684,6 +855,7 @@
fputs(buf, stderr);
goto finalize_multi_package_session;
}
+ const auto session_id_str = std::to_string(session_id);
fprintf(stdout, "Created child session ID %d.\n", session_id);
session_ids.push_back(session_id);
@@ -698,10 +870,15 @@
goto finalize_multi_package_session;
}
- std::string cmd = android::base::StringPrintf(
- "%s install-write -S %" PRIu64 " %d %d_%s -", install_cmd.c_str(),
- static_cast<uint64_t>(sb.st_size), session_id, i,
- android::base::Basename(split).c_str());
+ std::vector<std::string> cmd_args = {
+ install_cmd,
+ "install-write",
+ "-S",
+ std::to_string(sb.st_size),
+ session_id_str,
+ android::base::StringPrintf("%d_%s", i, android::base::Basename(file).c_str()),
+ "-",
+ };
unique_fd local_fd(adb_open(split.c_str(), O_RDONLY | O_CLOEXEC));
if (local_fd < 0) {
@@ -710,7 +887,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());
goto finalize_multi_package_session;
@@ -729,13 +906,11 @@
goto finalize_multi_package_session;
}
}
- all_session_ids += android::base::StringPrintf(" %d", session_id);
+ add_session_cmd_args.push_back(std::to_string(session_id));
}
- cmd = android::base::StringPrintf("%s install-add-session %d%s", install_cmd.c_str(),
- parent_session_id, all_session_ids.c_str());
{
- unique_fd fd(adb_connect(cmd, &error));
+ unique_fd fd = send_command(add_session_cmd_args, &error);
if (fd < 0) {
fprintf(stderr, "adb: connect error for install-add-session: %s\n", error.c_str());
goto finalize_multi_package_session;
@@ -744,7 +919,8 @@
}
if (strncmp("Success", buf, 7)) {
- fprintf(stderr, "adb: failed to link sessions (%s)\n", cmd.c_str());
+ fprintf(stderr, "adb: failed to link sessions (%s)\n",
+ android::base::Join(add_session_cmd_args, " ").c_str());
fputs(buf, stderr);
goto finalize_multi_package_session;
}
@@ -754,11 +930,14 @@
finalize_multi_package_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 == 0 ? "commit" : "abandon", parent_session_id);
+ std::vector<std::string> service_args = {
+ install_cmd,
+ success == 0 ? "install-commit" : "install-abandon",
+ parent_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;
@@ -779,10 +958,13 @@
session_ids.push_back(parent_session_id);
// try to abandon all remaining sessions
for (std::size_t i = 0; i < session_ids.size(); i++) {
- service = android::base::StringPrintf("%s install-abandon %d", install_cmd.c_str(),
- session_ids[i]);
+ std::vector<std::string> service_args = {
+ install_cmd,
+ "install-abandon",
+ std::to_string(session_ids[i]),
+ };
fprintf(stderr, "Attempting to abandon session ID %d\n", session_ids[i]);
- 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());
continue;
diff --git a/adb/client/adb_wifi.cpp b/adb/client/adb_wifi.cpp
index fa71028..61a9a48 100644
--- a/adb/client/adb_wifi.cpp
+++ b/adb/client/adb_wifi.cpp
@@ -179,17 +179,21 @@
void adb_wifi_pair_device(const std::string& host, const std::string& password,
std::string& response) {
- // Check the address for a valid address and port.
- std::string parsed_host;
- std::string err;
- int port = -1;
- if (!android::base::ParseNetAddress(host, &parsed_host, &port, nullptr, &err)) {
- response = "Failed to parse address for pairing: " + err;
- return;
- }
- if (port <= 0 || port > 65535) {
- response = "Invalid port while parsing address [" + host + "]";
- return;
+ auto mdns_info = mdns_get_pairing_service_info(host);
+
+ if (!mdns_info.has_value()) {
+ // Check the address for a valid address and port.
+ std::string parsed_host;
+ std::string err;
+ int port = -1;
+ if (!android::base::ParseNetAddress(host, &parsed_host, &port, nullptr, &err)) {
+ response = "Failed to parse address for pairing: " + err;
+ return;
+ }
+ if (port <= 0 || port > 65535) {
+ response = "Invalid port while parsing address [" + host + "]";
+ return;
+ }
}
auto priv_key = adb_auth_get_user_privkey();
@@ -220,7 +224,11 @@
PairingResultWaiter waiter;
std::unique_lock<std::mutex> lock(waiter.mutex_);
- if (!client->Start(host, waiter.OnResult, &waiter)) {
+ if (!client->Start(mdns_info.has_value()
+ ? android::base::StringPrintf("%s:%d", mdns_info->addr.c_str(),
+ mdns_info->port)
+ : host,
+ waiter.OnResult, &waiter)) {
response = "Failed: Unable to start pairing client.";
return;
}
diff --git a/adb/client/auth.cpp b/adb/client/auth.cpp
index 8738ce7..b674a81 100644
--- a/adb/client/auth.cpp
+++ b/adb/client/auth.cpp
@@ -145,12 +145,12 @@
std::lock_guard<std::mutex> lock(g_keys_mutex);
std::string fingerprint = hash_key(key.get());
- if (g_keys.find(fingerprint) != g_keys.end()) {
- LOG(INFO) << "ignoring already-loaded key: " << file;
- } else {
- LOG(INFO) << "Loaded fingerprint=[" << SHA256BitsToHexString(fingerprint) << "]";
+ bool already_loaded = (g_keys.find(fingerprint) != g_keys.end());
+ if (!already_loaded) {
g_keys[fingerprint] = std::move(key);
}
+ LOG(INFO) << (already_loaded ? "ignored already-loaded" : "loaded new") << " key from '" << file
+ << "' with fingerprint " << SHA256BitsToHexString(fingerprint);
return true;
}
@@ -159,23 +159,25 @@
struct stat st;
if (stat(path.c_str(), &st) != 0) {
- PLOG(ERROR) << "failed to stat '" << path << "'";
+ PLOG(ERROR) << "load_keys: failed to stat '" << path << "'";
return false;
}
if (S_ISREG(st.st_mode)) {
return load_key(path);
- } else if (S_ISDIR(st.st_mode)) {
+ }
+
+ if (S_ISDIR(st.st_mode)) {
if (!allow_dir) {
// inotify isn't recursive. It would break expectations to load keys in nested
// directories but not monitor them for new keys.
- LOG(WARNING) << "refusing to recurse into directory '" << path << "'";
+ LOG(WARNING) << "load_keys: refusing to recurse into directory '" << path << "'";
return false;
}
std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(path.c_str()), closedir);
if (!dir) {
- PLOG(ERROR) << "failed to open directory '" << path << "'";
+ PLOG(ERROR) << "load_keys: failed to open directory '" << path << "'";
return false;
}
@@ -189,7 +191,7 @@
}
if (!android::base::EndsWith(name, ".adb_key")) {
- LOG(INFO) << "skipping non-adb_key '" << path << "/" << name << "'";
+ LOG(INFO) << "skipped non-adb_key '" << path << "/" << name << "'";
continue;
}
@@ -198,7 +200,7 @@
return result;
}
- LOG(ERROR) << "unexpected type for '" << path << "': 0x" << std::hex << st.st_mode;
+ LOG(ERROR) << "load_keys: unexpected type for '" << path << "': 0x" << std::hex << st.st_mode;
return false;
}
@@ -502,6 +504,12 @@
}).detach();
}
+// Callback given to SSL_set_cert_cb to select a certificate when server requests
+// for a certificate. This is where the server will give us a CA-issuer list, and
+// figure out if the server knows any of our public keys. We currently always return
+// 1 here to indicate success, since we always try a key here (in the case of no auth).
+// See https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_set_cert_cb
+// for more details.
int adb_tls_set_certificate(SSL* ssl) {
LOG(INFO) << __func__;
diff --git a/adb/client/bugreport.cpp b/adb/client/bugreport.cpp
index 8ca44e8..e162aaa 100644
--- a/adb/client/bugreport.cpp
+++ b/adb/client/bugreport.cpp
@@ -282,5 +282,5 @@
bool Bugreport::DoSyncPull(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs,
const char* name) {
- return do_sync_pull(srcs, dst, copy_attrs, name);
+ return do_sync_pull(srcs, dst, copy_attrs, CompressionType::None, name);
}
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 081bac4..43772ba 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -50,6 +50,8 @@
#include <unistd.h>
#endif
+#include <google/protobuf/text_format.h>
+
#include "adb.h"
#include "adb_auth.h"
#include "adb_client.h"
@@ -57,6 +59,7 @@
#include "adb_io.h"
#include "adb_unique_fd.h"
#include "adb_utils.h"
+#include "app_processes.pb.h"
#include "bugreport.h"
#include "client/file_sync_client.h"
#include "commandline.h"
@@ -101,7 +104,8 @@
" connect HOST[:PORT] connect to a device via TCP/IP [default port=5555]\n"
" disconnect [HOST[:PORT]]\n"
" disconnect from given TCP/IP device [default port=5555], or all\n"
- " pair HOST[:PORT] pair with a device for secure TCP/IP communication\n"
+ " pair HOST[:PORT] [PAIRING CODE]\n"
+ " pair with a device for secure TCP/IP communication\n"
" forward --list list all forward socket connections\n"
" forward [--no-rebind] LOCAL REMOTE\n"
" forward socket connection using:\n"
@@ -124,17 +128,27 @@
" localfilesystem:<unix domain socket name>\n"
" reverse --remove REMOTE remove specific reverse socket connection\n"
" reverse --remove-all remove all reverse socket connections from device\n"
+ " mdns check check if mdns discovery is available\n"
+ " mdns services list all discovered services\n"
"\n"
"file transfer:\n"
- " push [--sync] LOCAL... REMOTE\n"
+ " push [--sync] [-z ALGORITHM] [-Z] LOCAL... REMOTE\n"
" copy local files/directories to device\n"
" --sync: only push files that are newer on the host than the device\n"
- " pull [-a] REMOTE... LOCAL\n"
+ " -n: dry run: push files to device without storing to the filesystem\n"
+ " -z: enable compression with a specified algorithm (any, none, brotli)\n"
+ " -Z: disable compression\n"
+ " pull [-a] [-z ALGORITHM] [-Z] REMOTE... LOCAL\n"
" copy files/dirs from device\n"
" -a: preserve file timestamp and mode\n"
- " sync [all|data|odm|oem|product|system|system_ext|vendor]\n"
+ " -z: enable compression with a specified algorithm (any, none, brotli)\n"
+ " -Z: disable compression\n"
+ " sync [-l] [-z ALGORITHM] [-Z] [all|data|odm|oem|product|system|system_ext|vendor]\n"
" sync a local build from $ANDROID_PRODUCT_OUT to the device (default all)\n"
+ " -n: dry run: push files to device without storing to the filesystem\n"
" -l: list files that would be copied, but don't copy them\n"
+ " -z: enable compression with a specified algorithm (any, none, brotli)\n"
+ " -Z: disable compression\n"
"\n"
"shell:\n"
" shell [-e ESCAPE] [-n] [-Tt] [-x] [COMMAND...]\n"
@@ -191,8 +205,8 @@
" generate adb public/private key; private key stored in FILE,\n"
"\n"
"scripting:\n"
- " wait-for[-TRANSPORT]-STATE\n"
- " wait for device to be in the given state\n"
+ " wait-for[-TRANSPORT]-STATE...\n"
+ " wait for device to be in a given state\n"
" STATE: device, recovery, rescue, sideload, bootloader, or disconnect\n"
" TRANSPORT: usb, local, or any [default=any]\n"
" get-state print offline | bootloader | device\n"
@@ -227,6 +241,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
}
@@ -661,18 +676,17 @@
}
static int adb_shell(int argc, const char** argv) {
- FeatureSet features;
- std::string error_message;
- if (!adb_get_feature_set(&features, &error_message)) {
- fprintf(stderr, "error: %s\n", error_message.c_str());
- return 1;
+ std::string error;
+ auto&& features = adb_get_feature_set(&error);
+ if (!features) {
+ error_exit("%s", error.c_str());
}
enum PtyAllocationMode { kPtyAuto, kPtyNo, kPtyYes, kPtyDefinitely };
// Defaults.
- char escape_char = '~'; // -e
- bool use_shell_protocol = CanUseFeature(features, kFeatureShell2); // -x
+ char escape_char = '~'; // -e
+ bool use_shell_protocol = CanUseFeature(*features, kFeatureShell2); // -x
PtyAllocationMode tty = use_shell_protocol ? kPtyAuto : kPtyDefinitely; // -t/-T
// Parse shell-specific command-line options.
@@ -748,7 +762,7 @@
if (!use_shell_protocol) {
if (shell_type_arg != kShellServiceArgPty) {
fprintf(stderr, "error: %s only supports allocating a pty\n",
- !CanUseFeature(features, kFeatureShell2) ? "device" : "-x");
+ !CanUseFeature(*features, kFeatureShell2) ? "device" : "-x");
return 1;
} else {
// If we're not using the shell protocol, the type argument must be empty.
@@ -768,14 +782,13 @@
}
static int adb_abb(int argc, const char** argv) {
- FeatureSet features;
- std::string error_message;
- if (!adb_get_feature_set(&features, &error_message)) {
- fprintf(stderr, "error: %s\n", error_message.c_str());
+ std::string error;
+ auto&& features = adb_get_feature_set(&error);
+ if (!features) {
+ error_exit("%s", error.c_str());
return 1;
}
-
- if (!CanUseFeature(features, kFeatureAbb)) {
+ if (!CanUseFeature(*features, kFeatureAbb)) {
error_exit("abb is not supported by the device");
}
@@ -1054,17 +1067,16 @@
static bool wait_for_device(const char* service,
std::optional<std::chrono::milliseconds> timeout = std::nullopt) {
std::vector<std::string> components = android::base::Split(service, "-");
- if (components.size() < 3 || components.size() > 4) {
+ if (components.size() < 3) {
fprintf(stderr, "adb: couldn't parse 'wait-for' command: %s\n", service);
return false;
}
- TransportType t;
- adb_get_transport(&t, nullptr, nullptr);
-
- // Was the caller vague about what they'd like us to wait for?
- // If so, check they weren't more specific in their choice of transport type.
- if (components.size() == 3) {
+ // If the first thing after "wait-for-" wasn't a TRANSPORT, insert whatever
+ // the current transport implies.
+ if (components[2] != "usb" && components[2] != "local" && components[2] != "any") {
+ TransportType t;
+ adb_get_transport(&t, nullptr, nullptr);
auto it = components.begin() + 2;
if (t == kTransportUsb) {
components.insert(it, "usb");
@@ -1073,23 +1085,9 @@
} else {
components.insert(it, "any");
}
- } else if (components[2] != "any" && components[2] != "local" && components[2] != "usb") {
- fprintf(stderr, "adb: unknown type %s; expected 'any', 'local', or 'usb'\n",
- components[2].c_str());
- return false;
}
- if (components[3] != "any" && components[3] != "bootloader" && components[3] != "device" &&
- components[3] != "recovery" && components[3] != "rescue" && components[3] != "sideload" &&
- components[3] != "disconnect") {
- fprintf(stderr,
- "adb: unknown state %s; "
- "expected 'any', 'bootloader', 'device', 'recovery', 'rescue', 'sideload', or "
- "'disconnect'\n",
- components[3].c_str());
- return false;
- }
-
+ // Stitch it back together and send it over...
std::string cmd = format_host_command(android::base::Join(components, "-").c_str());
if (timeout) {
std::thread([timeout]() {
@@ -1168,10 +1166,9 @@
// Use shell protocol if it's supported and the caller doesn't explicitly
// disable it.
if (!disable_shell_protocol) {
- FeatureSet features;
- std::string error;
- if (adb_get_feature_set(&features, &error)) {
- use_shell_protocol = CanUseFeature(features, kFeatureShell2);
+ auto&& features = adb_get_feature_set(nullptr);
+ if (features) {
+ use_shell_protocol = CanUseFeature(*features, kFeatureShell2);
} else {
// Device was unreachable.
attempt_connection = false;
@@ -1320,9 +1317,39 @@
return 0;
}
+static CompressionType parse_compression_type(const std::string& str, bool allow_numbers) {
+ if (allow_numbers) {
+ if (str == "0") {
+ return CompressionType::None;
+ } else if (str == "1") {
+ return CompressionType::Any;
+ }
+ }
+
+ if (str == "any") {
+ return CompressionType::Any;
+ } else if (str == "none") {
+ return CompressionType::None;
+ }
+
+ if (str == "brotli") {
+ return CompressionType::Brotli;
+ } else if (str == "lz4") {
+ return CompressionType::LZ4;
+ } else if (str == "zstd") {
+ return CompressionType::Zstd;
+ }
+
+ error_exit("unexpected compression type %s", str.c_str());
+}
+
static void parse_push_pull_args(const char** arg, int narg, std::vector<const char*>* srcs,
- const char** dst, bool* copy_attrs, bool* sync) {
+ const char** dst, bool* copy_attrs, bool* sync,
+ CompressionType* compression, bool* dry_run) {
*copy_attrs = false;
+ if (const char* adb_compression = getenv("ADB_COMPRESSION")) {
+ *compression = parse_compression_type(adb_compression, true);
+ }
srcs->clear();
bool ignore_flags = false;
@@ -1334,6 +1361,16 @@
// Silently ignore for backwards compatibility.
} else if (!strcmp(*arg, "-a")) {
*copy_attrs = true;
+ } else if (!strcmp(*arg, "-z")) {
+ if (narg < 2) {
+ error_exit("-z requires an argument");
+ }
+ *compression = parse_compression_type(*++arg, false);
+ --narg;
+ } else if (!strcmp(*arg, "-Z")) {
+ *compression = CompressionType::None;
+ } else if (dry_run && !strcmp(*arg, "-n")) {
+ *dry_run = true;
} else if (!strcmp(*arg, "--sync")) {
if (sync != nullptr) {
*sync = true;
@@ -1354,17 +1391,49 @@
}
}
-static int adb_connect_command(const std::string& command, TransportId* transport = nullptr) {
+static int adb_connect_command(const std::string& command, TransportId* transport,
+ StandardStreamsCallbackInterface* callback) {
std::string error;
unique_fd fd(adb_connect(transport, command, &error));
if (fd < 0) {
fprintf(stderr, "error: %s\n", error.c_str());
return 1;
}
- read_and_dump(fd);
+ read_and_dump(fd, false, callback);
return 0;
}
+static int adb_connect_command(const std::string& command, TransportId* transport = nullptr) {
+ return adb_connect_command(command, transport, &DEFAULT_STANDARD_STREAMS_CALLBACK);
+}
+
+// A class that prints out human readable form of the protobuf message for "track-app" service
+// (received in binary format).
+class TrackAppStreamsCallback : public DefaultStandardStreamsCallback {
+ public:
+ TrackAppStreamsCallback() : DefaultStandardStreamsCallback(nullptr, nullptr) {}
+
+ // Assume the buffer contains at least 4 bytes of valid data.
+ void OnStdout(const char* buffer, int length) override {
+ if (length < 4) return; // Unexpected length received. Do nothing.
+
+ adb::proto::AppProcesses binary_proto;
+ // The first 4 bytes are the length of remaining content in hexadecimal format.
+ binary_proto.ParseFromString(std::string(buffer + 4, length - 4));
+ char summary[24]; // The following string includes digits and 16 fixed characters.
+ int written = snprintf(summary, sizeof(summary), "Process count: %d\n",
+ binary_proto.process_size());
+ OnStream(nullptr, stdout, summary, written);
+
+ std::string string_proto;
+ google::protobuf::TextFormat::PrintToString(binary_proto, &string_proto);
+ OnStream(nullptr, stdout, string_proto.data(), string_proto.length());
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TrackAppStreamsCallback);
+};
+
static int adb_connect_command_bidirectional(const std::string& command) {
std::string error;
unique_fd fd(adb_connect(command, &error));
@@ -1423,6 +1492,26 @@
#endif
}
+static bool _is_valid_os_fd(int fd) {
+ // Disallow invalid FDs and stdin/out/err as well.
+ if (fd < 3) {
+ return false;
+ }
+#ifdef _WIN32
+ auto handle = (HANDLE)fd;
+ DWORD info = 0;
+ if (GetHandleInformation(handle, &info) == 0) {
+ return false;
+ }
+#else
+ int flags = fcntl(fd, F_GETFD);
+ if (flags == -1) {
+ return false;
+ }
+#endif
+ return true;
+}
+
int adb_commandline(int argc, const char** argv) {
bool no_daemon = false;
bool is_daemon = false;
@@ -1626,14 +1715,21 @@
}
printf("List of devices attached\n");
return adb_query_command(query);
- }
- else if (!strcmp(argv[0], "connect")) {
+ } else if (!strcmp(argv[0], "transport-id")) {
+ TransportId transport_id;
+ std::string error;
+ unique_fd fd(adb_connect(&transport_id, "host:features", &error, true));
+ if (fd == -1) {
+ error_exit("%s", error.c_str());
+ }
+ printf("%" PRIu64 "\n", transport_id);
+ return 0;
+ } else if (!strcmp(argv[0], "connect")) {
if (argc != 2) error_exit("usage: adb connect HOST[:PORT]");
std::string query = android::base::StringPrintf("host:connect:%s", argv[1]);
return adb_query_command(query);
- }
- else if (!strcmp(argv[0], "disconnect")) {
+ } else if (!strcmp(argv[0], "disconnect")) {
if (argc > 2) error_exit("usage: adb disconnect [HOST[:PORT]]");
std::string query = android::base::StringPrintf("host:disconnect:%s",
@@ -1642,13 +1738,17 @@
} else if (!strcmp(argv[0], "abb")) {
return adb_abb(argc, argv);
} else if (!strcmp(argv[0], "pair")) {
- if (argc != 2) error_exit("usage: adb pair <host>[:<port>]");
+ if (argc < 2 || argc > 3) error_exit("usage: adb pair HOST[:PORT] [PAIRING CODE]");
std::string password;
- printf("Enter pairing code: ");
- fflush(stdout);
- if (!std::getline(std::cin, password) || password.empty()) {
- error_exit("No pairing code provided");
+ if (argc == 2) {
+ printf("Enter pairing code: ");
+ fflush(stdout);
+ if (!std::getline(std::cin, password) || password.empty()) {
+ error_exit("No pairing code provided");
+ }
+ } else {
+ password = argv[2];
}
std::string query =
android::base::StringPrintf("host:pair:%s:%s", password.c_str(), argv[1]);
@@ -1730,14 +1830,13 @@
}
return adb_connect_command(android::base::StringPrintf("tcpip:%d", port));
} else if (!strcmp(argv[0], "remount")) {
- FeatureSet features;
std::string error;
- if (!adb_get_feature_set(&features, &error)) {
- fprintf(stderr, "error: %s\n", error.c_str());
- return 1;
+ auto&& features = adb_get_feature_set(&error);
+ if (!features) {
+ error_exit("%s", error.c_str());
}
- if (CanUseFeature(features, kFeatureRemountShell)) {
+ if (CanUseFeature(*features, kFeatureRemountShell)) {
std::vector<const char*> args = {"shell"};
args.insert(args.cend(), argv, argv + argc);
return adb_shell_noinput(args.size(), args.data());
@@ -1828,6 +1927,29 @@
ReadOrderlyShutdown(fd);
return 0;
+ } else if (!strcmp(argv[0], "mdns")) {
+ --argc;
+ if (argc < 1) error_exit("mdns requires an argument");
+ ++argv;
+
+ std::string error;
+ if (!adb_check_server_version(&error)) {
+ error_exit("failed to check server version: %s", error.c_str());
+ }
+
+ std::string query = "host:mdns:";
+ if (!strcmp(argv[0], "check")) {
+ if (argc != 1) error_exit("mdns %s doesn't take any arguments", argv[0]);
+ query += "check";
+ } else if (!strcmp(argv[0], "services")) {
+ if (argc != 1) error_exit("mdns %s doesn't take any arguments", argv[0]);
+ query += "services";
+ printf("List of discovered mdns services\n");
+ } else {
+ error_exit("unknown mdns command [%s]", argv[0]);
+ }
+
+ return adb_query_command(query);
}
/* do_sync_*() commands */
else if (!strcmp(argv[0], "ls")) {
@@ -1836,20 +1958,25 @@
} else if (!strcmp(argv[0], "push")) {
bool copy_attrs = false;
bool sync = false;
+ bool dry_run = false;
+ CompressionType compression = CompressionType::Any;
std::vector<const char*> srcs;
const char* dst = nullptr;
- parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs, &sync);
+ parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs, &sync, &compression,
+ &dry_run);
if (srcs.empty() || !dst) error_exit("push requires an argument");
- return do_sync_push(srcs, dst, sync) ? 0 : 1;
+ return do_sync_push(srcs, dst, sync, compression, dry_run) ? 0 : 1;
} else if (!strcmp(argv[0], "pull")) {
bool copy_attrs = false;
+ CompressionType compression = CompressionType::Any;
std::vector<const char*> srcs;
const char* dst = ".";
- parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs, nullptr);
+ parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs, nullptr, &compression,
+ nullptr);
if (srcs.empty()) error_exit("pull requires an argument");
- return do_sync_pull(srcs, dst, copy_attrs) ? 0 : 1;
+ return do_sync_pull(srcs, dst, copy_attrs, compression) ? 0 : 1;
} else if (!strcmp(argv[0], "install")) {
if (argc < 2) error_exit("install requires an argument");
return install_app(argc, argv);
@@ -1865,18 +1992,41 @@
} else if (!strcmp(argv[0], "sync")) {
std::string src;
bool list_only = false;
- if (argc < 2) {
- // No partition specified: sync all of them.
- } else if (argc >= 2 && strcmp(argv[1], "-l") == 0) {
- list_only = true;
- if (argc == 3) src = argv[2];
- } else if (argc == 2) {
- src = argv[1];
- } else {
- error_exit("usage: adb sync [-l] [PARTITION]");
+ bool dry_run = false;
+ CompressionType compression = CompressionType::Any;
+
+ if (const char* adb_compression = getenv("ADB_COMPRESSION"); adb_compression) {
+ compression = parse_compression_type(adb_compression, true);
}
- if (src.empty()) src = "all";
+ int opt;
+ while ((opt = getopt(argc, const_cast<char**>(argv), "lnz:Z")) != -1) {
+ switch (opt) {
+ case 'l':
+ list_only = true;
+ break;
+ case 'n':
+ dry_run = true;
+ break;
+ case 'z':
+ compression = parse_compression_type(optarg, false);
+ break;
+ case 'Z':
+ compression = CompressionType::None;
+ break;
+ default:
+ error_exit("usage: adb sync [-l] [-n] [-z ALGORITHM] [-Z] [PARTITION]");
+ }
+ }
+
+ if (optind == argc) {
+ src = "all";
+ } else if (optind + 1 == argc) {
+ src = argv[optind];
+ } else {
+ error_exit("usage: adb sync [-l] [-n] [-z ALGORITHM] [-Z] [PARTITION]");
+ }
+
std::vector<std::string> partitions{"data", "odm", "oem", "product",
"system", "system_ext", "vendor"};
bool found = false;
@@ -1885,7 +2035,9 @@
std::string src_dir{product_file(partition)};
if (!directory_exists(src_dir)) continue;
found = true;
- if (!do_sync_sync(src_dir, "/" + partition, list_only)) return 1;
+ if (!do_sync_sync(src_dir, "/" + partition, list_only, compression, dry_run)) {
+ return 1;
+ }
}
}
if (!found) error_exit("don't know how to sync %s partition", src.c_str());
@@ -1925,6 +2077,17 @@
return adb_connect_command("jdwp");
} else if (!strcmp(argv[0], "track-jdwp")) {
return adb_connect_command("track-jdwp");
+ } else if (!strcmp(argv[0], "track-app")) {
+ std::string error;
+ auto&& features = adb_get_feature_set(&error);
+ if (!features) {
+ error_exit("%s", error.c_str());
+ }
+ if (!CanUseFeature(*features, kFeatureTrackApp)) {
+ error_exit("track-app is not supported by the device");
+ }
+ TrackAppStreamsCallback callback;
+ return adb_connect_command("track-app", nullptr, &callback);
} else if (!strcmp(argv[0], "track-devices")) {
if (argc > 2 || (argc == 2 && strcmp(argv[1], "-l"))) {
error_exit("usage: adb track-devices [-l]");
@@ -1946,15 +2109,14 @@
return 0;
} else if (!strcmp(argv[0], "features")) {
// Only list the features common to both the adb client and the device.
- FeatureSet features;
std::string error;
- if (!adb_get_feature_set(&features, &error)) {
- fprintf(stderr, "error: %s\n", error.c_str());
- return 1;
+ auto&& features = adb_get_feature_set(&error);
+ if (!features) {
+ error_exit("%s", error.c_str());
}
- for (const std::string& name : features) {
- if (CanUseFeature(features, name)) {
+ for (const std::string& name : *features) {
+ if (CanUseFeature(*features, name)) {
printf("%s\n", name.c_str());
}
}
@@ -1977,17 +2139,28 @@
}
}
} else if (!strcmp(argv[0], "inc-server")) {
- if (argc < 3) {
- error_exit("usage: adb inc-server FD FILE1 FILE2 ...");
+ if (argc < 4) {
+#ifdef _WIN32
+ error_exit("usage: adb inc-server CONNECTION_HANDLE OUTPUT_HANDLE FILE1 FILE2 ...");
+#else
+ error_exit("usage: adb inc-server CONNECTION_FD OUTPUT_FD FILE1 FILE2 ...");
+#endif
}
- int fd = atoi(argv[1]);
- if (fd < 3) {
- // Disallow invalid FDs and stdin/out/err as well.
- error_exit("Invalid fd number given: %d", fd);
+ int connection_fd = atoi(argv[1]);
+ if (!_is_valid_os_fd(connection_fd)) {
+ error_exit("Invalid connection_fd number given: %d", connection_fd);
}
- fd = adb_register_socket(fd);
- close_on_exec(fd);
- return incremental::serve(fd, argc - 2, argv + 2);
+
+ connection_fd = adb_register_socket(connection_fd);
+ close_on_exec(connection_fd);
+
+ int output_fd = atoi(argv[2]);
+ if (!_is_valid_os_fd(output_fd)) {
+ error_exit("Invalid output_fd number given: %d", output_fd);
+ }
+ output_fd = adb_register_socket(output_fd);
+ close_on_exec(output_fd);
+ return incremental::serve(connection_fd, output_fd, argc - 3, argv + 3);
}
error_exit("unknown command %s", argv[0]);
diff --git a/adb/client/fastdeploy.cpp b/adb/client/fastdeploy.cpp
index c5fc12f..bc4b91b 100644
--- a/adb/client/fastdeploy.cpp
+++ b/adb/client/fastdeploy.cpp
@@ -112,7 +112,7 @@
// but can't be removed until after the push.
unix_close(tf.release());
- if (!do_sync_push(srcs, dst, sync)) {
+ if (!do_sync_push(srcs, dst, sync, CompressionType::Any, false)) {
error_exit("Failed to push fastdeploy agent to device.");
}
}
diff --git a/adb/client/file_sync_client.cpp b/adb/client/file_sync_client.cpp
index 922f2ba..8bbe2a8 100644
--- a/adb/client/file_sync_client.cpp
+++ b/adb/client/file_sync_client.cpp
@@ -29,10 +29,12 @@
#include <utime.h>
#include <chrono>
+#include <deque>
#include <functional>
#include <memory>
#include <sstream>
#include <string>
+#include <variant>
#include <vector>
#include "sysdeps.h"
@@ -41,6 +43,7 @@
#include "adb_client.h"
#include "adb_io.h"
#include "adb_utils.h"
+#include "compression_utils.h"
#include "file_sync_protocol.h"
#include "line_printer.h"
#include "sysdeps/errno.h"
@@ -52,6 +55,8 @@
#include <android-base/strings.h>
#include <android-base/stringprintf.h>
+using namespace std::literals;
+
typedef void(sync_ls_cb)(unsigned mode, uint64_t size, uint64_t time, const char* name);
struct syncsendbuf {
@@ -112,6 +117,11 @@
uint64_t bytes_expected;
bool expect_multiple_files;
+ private:
+ std::string last_progress_str;
+ std::chrono::steady_clock::time_point last_progress_time;
+
+ public:
TransferLedger() {
Reset();
}
@@ -131,6 +141,8 @@
files_skipped = 0;
bytes_transferred = 0;
bytes_expected = 0;
+ last_progress_str.clear();
+ last_progress_time = {};
}
std::string TransferRate() {
@@ -150,6 +162,12 @@
void ReportProgress(LinePrinter& lp, const std::string& file, uint64_t file_copied_bytes,
uint64_t file_total_bytes) {
+ static constexpr auto kProgressReportInterval = 100ms;
+
+ auto now = std::chrono::steady_clock::now();
+ if (now < last_progress_time + kProgressReportInterval) {
+ return;
+ }
char overall_percentage_str[5] = "?";
if (bytes_expected != 0 && bytes_transferred <= bytes_expected) {
int overall_percentage = static_cast<int>(bytes_transferred * 100 / bytes_expected);
@@ -180,7 +198,11 @@
android::base::StringPrintf("[%4s] %s", overall_percentage_str, file.c_str());
}
}
- lp.Print(output, LinePrinter::LineType::INFO);
+ if (output != last_progress_str) {
+ lp.Print(output, LinePrinter::LineType::INFO);
+ last_progress_str = std::move(output);
+ last_progress_time = now;
+ }
}
void ReportTransferRate(LinePrinter& lp, const std::string& name, TransferDirection direction) {
@@ -203,15 +225,24 @@
class SyncConnection {
public:
- SyncConnection() : expect_done_(false) {
+ SyncConnection() : acknowledgement_buffer_(sizeof(sync_status) + SYNC_DATA_MAX) {
+ acknowledgement_buffer_.resize(0);
max = SYNC_DATA_MAX; // TODO: decide at runtime.
std::string error;
- if (!adb_get_feature_set(&features_, &error)) {
+ auto&& features = adb_get_feature_set(&error);
+ if (!features) {
Error("failed to get feature set: %s", error.c_str());
} else {
- have_stat_v2_ = CanUseFeature(features_, kFeatureStat2);
- have_ls_v2_ = CanUseFeature(features_, kFeatureLs2);
+ features_ = &*features;
+ have_stat_v2_ = CanUseFeature(*features, kFeatureStat2);
+ have_ls_v2_ = CanUseFeature(*features, kFeatureLs2);
+ have_sendrecv_v2_ = CanUseFeature(*features, kFeatureSendRecv2);
+ have_sendrecv_v2_brotli_ = CanUseFeature(*features, kFeatureSendRecv2Brotli);
+ have_sendrecv_v2_lz4_ = CanUseFeature(*features, kFeatureSendRecv2LZ4);
+ have_sendrecv_v2_zstd_ = CanUseFeature(*features, kFeatureSendRecv2Zstd);
+ have_sendrecv_v2_dry_run_send_ = CanUseFeature(*features, kFeatureSendRecv2DryRunSend);
+ std::string error;
fd.reset(adb_connect("sync:", &error));
if (fd < 0) {
Error("connect failed: %s", error.c_str());
@@ -235,20 +266,32 @@
line_printer_.KeepInfoLine();
}
- const FeatureSet& Features() const { return features_; }
+ bool HaveSendRecv2() const { return have_sendrecv_v2_; }
+ bool HaveSendRecv2Brotli() const { return have_sendrecv_v2_brotli_; }
+ bool HaveSendRecv2LZ4() const { return have_sendrecv_v2_lz4_; }
+ bool HaveSendRecv2Zstd() const { return have_sendrecv_v2_zstd_; }
+ bool HaveSendRecv2DryRunSend() const { return have_sendrecv_v2_dry_run_send_; }
+
+ // Resolve a compression type which might be CompressionType::Any to a specific compression
+ // algorithm.
+ CompressionType ResolveCompressionType(CompressionType compression) const {
+ if (compression == CompressionType::Any) {
+ if (HaveSendRecv2Zstd()) {
+ return CompressionType::Zstd;
+ } else if (HaveSendRecv2LZ4()) {
+ return CompressionType::LZ4;
+ } else if (HaveSendRecv2Brotli()) {
+ return CompressionType::Brotli;
+ }
+ return CompressionType::None;
+ }
+ return compression;
+ }
+
+ const FeatureSet& Features() const { return *features_; }
bool IsValid() { return fd >= 0; }
- bool ReceivedError(const char* from, const char* to) {
- adb_pollfd pfd = {.fd = fd.get(), .events = POLLIN};
- int rc = adb_poll(&pfd, 1, 0);
- if (rc < 0) {
- Error("failed to poll: %s", strerror(errno));
- return true;
- }
- return rc != 0;
- }
-
void NewTransfer() {
current_ledger_.Reset();
}
@@ -258,6 +301,11 @@
global_ledger_.bytes_transferred += bytes;
}
+ void RecordFileSent(std::string from, std::string to) {
+ RecordFilesTransferred(1);
+ deferred_acknowledgements_.emplace_back(std::move(from), std::move(to));
+ }
+
void RecordFilesTransferred(size_t files) {
current_ledger_.files_transferred += files;
global_ledger_.files_transferred += files;
@@ -283,39 +331,136 @@
}
}
- bool SendRequest(int id, const char* path_and_mode) {
- size_t path_length = strlen(path_and_mode);
- if (path_length > 1024) {
- Error("SendRequest failed: path too long: %zu", path_length);
+ bool SendRequest(int id, const std::string& path) {
+ if (path.length() > 1024) {
+ Error("SendRequest failed: path too long: %zu", path.length());
errno = ENAMETOOLONG;
return false;
}
// Sending header and payload in a single write makes a noticeable
// difference to "adb sync" performance.
- std::vector<char> buf(sizeof(SyncRequest) + path_length);
+ std::vector<char> buf(sizeof(SyncRequest) + path.length());
SyncRequest* req = reinterpret_cast<SyncRequest*>(&buf[0]);
req->id = id;
- req->path_length = path_length;
+ req->path_length = path.length();
char* data = reinterpret_cast<char*>(req + 1);
- memcpy(data, path_and_mode, path_length);
-
- return WriteFdExactly(fd, &buf[0], buf.size());
+ memcpy(data, path.data(), path.length());
+ return WriteFdExactly(fd, buf.data(), buf.size());
}
- bool SendStat(const char* path_and_mode) {
+ bool SendSend2(std::string_view path, mode_t mode, CompressionType compression, bool dry_run) {
+ if (path.length() > 1024) {
+ Error("SendRequest failed: path too long: %zu", path.length());
+ errno = ENAMETOOLONG;
+ return false;
+ }
+
+ Block buf;
+
+ SyncRequest req;
+ req.id = ID_SEND_V2;
+ req.path_length = path.length();
+
+ syncmsg msg;
+ msg.send_v2_setup.id = ID_SEND_V2;
+ msg.send_v2_setup.mode = mode;
+ msg.send_v2_setup.flags = 0;
+ switch (compression) {
+ case CompressionType::None:
+ break;
+
+ case CompressionType::Brotli:
+ msg.send_v2_setup.flags = kSyncFlagBrotli;
+ break;
+
+ case CompressionType::LZ4:
+ msg.send_v2_setup.flags = kSyncFlagLZ4;
+ break;
+
+ case CompressionType::Zstd:
+ msg.send_v2_setup.flags = kSyncFlagZstd;
+ break;
+
+ case CompressionType::Any:
+ LOG(FATAL) << "unexpected CompressionType::Any";
+ }
+
+ if (dry_run) {
+ msg.send_v2_setup.flags |= kSyncFlagDryRun;
+ }
+
+ buf.resize(sizeof(SyncRequest) + path.length() + sizeof(msg.send_v2_setup));
+
+ void* p = buf.data();
+
+ p = mempcpy(p, &req, sizeof(SyncRequest));
+ p = mempcpy(p, path.data(), path.length());
+ p = mempcpy(p, &msg.send_v2_setup, sizeof(msg.send_v2_setup));
+
+ return WriteFdExactly(fd, buf.data(), buf.size());
+ }
+
+ bool SendRecv2(const std::string& path, CompressionType compression) {
+ if (path.length() > 1024) {
+ Error("SendRequest failed: path too long: %zu", path.length());
+ errno = ENAMETOOLONG;
+ return false;
+ }
+
+ Block buf;
+
+ SyncRequest req;
+ req.id = ID_RECV_V2;
+ req.path_length = path.length();
+
+ syncmsg msg;
+ msg.recv_v2_setup.id = ID_RECV_V2;
+ msg.recv_v2_setup.flags = 0;
+ switch (compression) {
+ case CompressionType::None:
+ break;
+
+ case CompressionType::Brotli:
+ msg.recv_v2_setup.flags |= kSyncFlagBrotli;
+ break;
+
+ case CompressionType::LZ4:
+ msg.recv_v2_setup.flags |= kSyncFlagLZ4;
+ break;
+
+ case CompressionType::Zstd:
+ msg.recv_v2_setup.flags |= kSyncFlagZstd;
+ break;
+
+ case CompressionType::Any:
+ LOG(FATAL) << "unexpected CompressionType::Any";
+ }
+
+ buf.resize(sizeof(SyncRequest) + path.length() + sizeof(msg.recv_v2_setup));
+
+ void* p = buf.data();
+
+ p = mempcpy(p, &req, sizeof(SyncRequest));
+ p = mempcpy(p, path.data(), path.length());
+ p = mempcpy(p, &msg.recv_v2_setup, sizeof(msg.recv_v2_setup));
+
+ return WriteFdExactly(fd, buf.data(), buf.size());
+ }
+
+ bool SendStat(const std::string& path) {
if (!have_stat_v2_) {
errno = ENOTSUP;
return false;
}
- return SendRequest(ID_STAT_V2, path_and_mode);
+ return SendRequest(ID_STAT_V2, path);
}
- bool SendLstat(const char* path_and_mode) {
+ bool SendLstat(const std::string& path) {
if (have_stat_v2_) {
- return SendRequest(ID_LSTAT_V2, path_and_mode);
+ return SendRequest(ID_LSTAT_V2, path);
} else {
- return SendRequest(ID_LSTAT_V1, path_and_mode);
+ return SendRequest(ID_LSTAT_V1, path);
}
}
@@ -355,8 +500,8 @@
}
if (msg.stat_v1.id != ID_LSTAT_V1) {
- PLOG(FATAL) << "protocol fault: stat response has wrong message id: "
- << msg.stat_v1.id;
+ LOG(FATAL) << "protocol fault: stat response has wrong message id: "
+ << msg.stat_v1.id;
}
if (msg.stat_v1.mode == 0 && msg.stat_v1.size == 0 && msg.stat_v1.mtime == 0) {
@@ -374,7 +519,7 @@
return true;
}
- bool SendLs(const char* path) {
+ bool SendLs(const std::string& path) {
return SendRequest(have_ls_v2_ ? ID_LIST_V2 : ID_LIST_V1, path);
}
@@ -415,28 +560,31 @@
// Sending header, payload, and footer in a single write makes a huge
// difference to "adb sync" performance.
- bool SendSmallFile(const char* path_and_mode,
- const char* lpath, const char* rpath,
- unsigned mtime,
- const char* data, size_t data_length) {
- size_t path_length = strlen(path_and_mode);
- if (path_length > 1024) {
- Error("SendSmallFile failed: path too long: %zu", path_length);
+ bool SendSmallFile(const std::string& path, mode_t mode, const std::string& lpath,
+ const std::string& rpath, unsigned mtime, const char* data,
+ size_t data_length, bool dry_run) {
+ if (dry_run) {
+ // We need to use send v2 for dry run.
+ return SendLargeFile(path, mode, lpath, rpath, mtime, CompressionType::None, dry_run);
+ }
+
+ std::string path_and_mode = android::base::StringPrintf("%s,%d", path.c_str(), mode);
+ if (path_and_mode.length() > 1024) {
+ Error("SendSmallFile failed: path too long: %zu", path_and_mode.length());
errno = ENAMETOOLONG;
return false;
}
- std::vector<char> buf(sizeof(SyncRequest) + path_length +
- sizeof(SyncRequest) + data_length +
- sizeof(SyncRequest));
+ std::vector<char> buf(sizeof(SyncRequest) + path_and_mode.length() + sizeof(SyncRequest) +
+ data_length + sizeof(SyncRequest));
char* p = &buf[0];
SyncRequest* req_send = reinterpret_cast<SyncRequest*>(p);
- req_send->id = ID_SEND;
- req_send->path_length = path_length;
+ req_send->id = ID_SEND_V1;
+ req_send->path_length = path_and_mode.length();
p += sizeof(SyncRequest);
- memcpy(p, path_and_mode, path_length);
- p += path_length;
+ memcpy(p, path_and_mode.data(), path_and_mode.size());
+ p += path_and_mode.length();
SyncRequest* req_data = reinterpret_cast<SyncRequest*>(p);
req_data->id = ID_DATA;
@@ -451,43 +599,156 @@
p += sizeof(SyncRequest);
WriteOrDie(lpath, rpath, &buf[0], (p - &buf[0]));
- expect_done_ = true;
- // RecordFilesTransferred gets called in CopyDone.
+ RecordFileSent(lpath, rpath);
RecordBytesTransferred(data_length);
ReportProgress(rpath, data_length, data_length);
return true;
}
- bool SendLargeFile(const char* path_and_mode,
- const char* lpath, const char* rpath,
- unsigned mtime) {
- if (!SendRequest(ID_SEND, path_and_mode)) {
- Error("failed to send ID_SEND message '%s': %s", path_and_mode, strerror(errno));
+ bool SendLargeFile(const std::string& path, mode_t mode, const std::string& lpath,
+ const std::string& rpath, unsigned mtime, CompressionType compression,
+ bool dry_run) {
+ if (dry_run && !HaveSendRecv2DryRunSend()) {
+ Error("dry-run not supported by the device");
+ return false;
+ }
+
+ if (!HaveSendRecv2()) {
+ return SendLargeFileLegacy(path, mode, lpath, rpath, mtime);
+ }
+
+ compression = ResolveCompressionType(compression);
+
+ if (!SendSend2(path, mode, compression, dry_run)) {
+ Error("failed to send ID_SEND_V2 message '%s': %s", path.c_str(), strerror(errno));
return false;
}
struct stat st;
- if (stat(lpath, &st) == -1) {
- Error("cannot stat '%s': %s", lpath, strerror(errno));
+ if (stat(lpath.c_str(), &st) == -1) {
+ Error("cannot stat '%s': %s", lpath.c_str(), strerror(errno));
return false;
}
uint64_t total_size = st.st_size;
uint64_t bytes_copied = 0;
- unique_fd lfd(adb_open(lpath, O_RDONLY));
+ unique_fd lfd(adb_open(lpath.c_str(), O_RDONLY | O_CLOEXEC));
if (lfd < 0) {
- Error("opening '%s' locally failed: %s", lpath, strerror(errno));
+ Error("opening '%s' locally failed: %s", lpath.c_str(), strerror(errno));
return false;
}
syncsendbuf sbuf;
sbuf.id = ID_DATA;
+
+ std::variant<std::monostate, NullEncoder, BrotliEncoder, LZ4Encoder, ZstdEncoder>
+ encoder_storage;
+ Encoder* encoder = nullptr;
+ switch (compression) {
+ case CompressionType::None:
+ encoder = &encoder_storage.emplace<NullEncoder>(SYNC_DATA_MAX);
+ break;
+
+ case CompressionType::Brotli:
+ encoder = &encoder_storage.emplace<BrotliEncoder>(SYNC_DATA_MAX);
+ break;
+
+ case CompressionType::LZ4:
+ encoder = &encoder_storage.emplace<LZ4Encoder>(SYNC_DATA_MAX);
+ break;
+
+ case CompressionType::Zstd:
+ encoder = &encoder_storage.emplace<ZstdEncoder>(SYNC_DATA_MAX);
+ break;
+
+ case CompressionType::Any:
+ LOG(FATAL) << "unexpected CompressionType::Any";
+ }
+
+ bool sending = true;
+ while (sending) {
+ Block input(SYNC_DATA_MAX);
+ int r = adb_read(lfd.get(), input.data(), input.size());
+ if (r < 0) {
+ Error("reading '%s' locally failed: %s", lpath.c_str(), strerror(errno));
+ return false;
+ }
+
+ if (r == 0) {
+ encoder->Finish();
+ } else {
+ input.resize(r);
+ encoder->Append(std::move(input));
+ RecordBytesTransferred(r);
+ bytes_copied += r;
+ ReportProgress(rpath, bytes_copied, total_size);
+ }
+
+ while (true) {
+ Block output;
+ EncodeResult result = encoder->Encode(&output);
+ if (result == EncodeResult::Error) {
+ Error("compressing '%s' locally failed", lpath.c_str());
+ return false;
+ }
+
+ if (!output.empty()) {
+ sbuf.size = output.size();
+ memcpy(sbuf.data, output.data(), output.size());
+ WriteOrDie(lpath, rpath, &sbuf, sizeof(SyncRequest) + output.size());
+ }
+
+ if (result == EncodeResult::Done) {
+ sending = false;
+ break;
+ } else if (result == EncodeResult::NeedInput) {
+ break;
+ } else if (result == EncodeResult::MoreOutput) {
+ continue;
+ }
+ }
+ }
+
+ syncmsg msg;
+ msg.data.id = ID_DONE;
+ msg.data.size = mtime;
+ RecordFileSent(lpath, rpath);
+ return WriteOrDie(lpath, rpath, &msg.data, sizeof(msg.data));
+ }
+
+ bool SendLargeFileLegacy(const std::string& path, mode_t mode, const std::string& lpath,
+ const std::string& rpath, unsigned mtime) {
+ std::string path_and_mode = android::base::StringPrintf("%s,%d", path.c_str(), mode);
+ if (!SendRequest(ID_SEND_V1, path_and_mode)) {
+ Error("failed to send ID_SEND_V1 message '%s': %s", path_and_mode.c_str(),
+ strerror(errno));
+ return false;
+ }
+
+ struct stat st;
+ if (stat(lpath.c_str(), &st) == -1) {
+ Error("cannot stat '%s': %s", lpath.c_str(), strerror(errno));
+ return false;
+ }
+
+ uint64_t total_size = st.st_size;
+ uint64_t bytes_copied = 0;
+
+ unique_fd lfd(adb_open(lpath.c_str(), O_RDONLY | O_CLOEXEC));
+ if (lfd < 0) {
+ Error("opening '%s' locally failed: %s", lpath.c_str(), strerror(errno));
+ return false;
+ }
+
+ syncsendbuf sbuf;
+ sbuf.id = ID_DATA;
+
while (true) {
- int bytes_read = adb_read(lfd, sbuf.data, max - sizeof(SyncRequest));
+ int bytes_read = adb_read(lfd, sbuf.data, max);
if (bytes_read == -1) {
- Error("reading '%s' locally failed: %s", lpath, strerror(errno));
+ Error("reading '%s' locally failed: %s", lpath.c_str(), strerror(errno));
return false;
} else if (bytes_read == 0) {
break;
@@ -498,59 +759,119 @@
RecordBytesTransferred(bytes_read);
bytes_copied += bytes_read;
-
- // Check to see if we've received an error from the other side.
- if (ReceivedError(lpath, rpath)) {
- break;
- }
-
ReportProgress(rpath, bytes_copied, total_size);
}
syncmsg msg;
msg.data.id = ID_DONE;
msg.data.size = mtime;
- expect_done_ = true;
-
- // RecordFilesTransferred gets called in CopyDone.
+ RecordFileSent(lpath, rpath);
return WriteOrDie(lpath, rpath, &msg.data, sizeof(msg.data));
}
- bool CopyDone(const char* from, const char* to) {
- syncmsg msg;
- if (!ReadFdExactly(fd, &msg.status, sizeof(msg.status))) {
- Error("failed to copy '%s' to '%s': couldn't read from device", from, to);
- return false;
- }
- if (msg.status.id == ID_OKAY) {
- if (expect_done_) {
- expect_done_ = false;
- RecordFilesTransferred(1);
- return true;
- } else {
- Error("failed to copy '%s' to '%s': received premature success", from, to);
- return true;
- }
- }
- if (msg.status.id != ID_FAIL) {
- Error("failed to copy '%s' to '%s': unknown reason %d", from, to, msg.status.id);
- return false;
- }
- return ReportCopyFailure(from, to, msg);
- }
-
- bool ReportCopyFailure(const char* from, const char* to, const syncmsg& msg) {
+ bool ReportCopyFailure(const std::string& from, const std::string& to, const syncmsg& msg) {
std::vector<char> buf(msg.status.msglen + 1);
if (!ReadFdExactly(fd, &buf[0], msg.status.msglen)) {
- Error("failed to copy '%s' to '%s'; failed to read reason (!): %s",
- from, to, strerror(errno));
+ Error("failed to copy '%s' to '%s'; failed to read reason (!): %s", from.c_str(),
+ to.c_str(), strerror(errno));
return false;
}
buf[msg.status.msglen] = 0;
- Error("failed to copy '%s' to '%s': remote %s", from, to, &buf[0]);
+ Error("failed to copy '%s' to '%s': remote %s", from.c_str(), to.c_str(), &buf[0]);
return false;
}
+ void CopyDone() { deferred_acknowledgements_.pop_front(); }
+
+ void ReportDeferredCopyFailure(const std::string& msg) {
+ auto& [from, to] = deferred_acknowledgements_.front();
+ Error("failed to copy '%s' to '%s': remote %s", from.c_str(), to.c_str(), msg.c_str());
+ deferred_acknowledgements_.pop_front();
+ }
+
+ bool ReadAcknowledgements(bool read_all = false) {
+ // We need to read enough such that adbd's intermediate socket's write buffer can't be
+ // full. The default buffer on Linux is 212992 bytes, but there's 576 bytes of bookkeeping
+ // overhead per write. The worst case scenario is a continuous string of failures, since
+ // each logical packet is divided into two writes. If our packet size if conservatively 512
+ // bytes long, this leaves us with space for 128 responses.
+ constexpr size_t max_deferred_acks = 128;
+ auto& buf = acknowledgement_buffer_;
+ adb_pollfd pfd = {.fd = fd.get(), .events = POLLIN};
+ while (!deferred_acknowledgements_.empty()) {
+ bool should_block = read_all || deferred_acknowledgements_.size() >= max_deferred_acks;
+
+ ssize_t rc = adb_poll(&pfd, 1, should_block ? -1 : 0);
+ if (rc == 0) {
+ CHECK(!should_block);
+ return true;
+ }
+
+ if (acknowledgement_buffer_.size() < sizeof(sync_status)) {
+ const ssize_t header_bytes_left = sizeof(sync_status) - buf.size();
+ ssize_t rc = adb_read(fd, buf.end(), header_bytes_left);
+ if (rc <= 0) {
+ Error("failed to read copy response");
+ return false;
+ }
+
+ buf.resize(buf.size() + rc);
+ if (rc != header_bytes_left) {
+ // Early exit if we run out of data in the socket.
+ return true;
+ }
+
+ if (!should_block) {
+ // We don't want to read again yet, because the socket might be empty.
+ continue;
+ }
+ }
+
+ auto* hdr = reinterpret_cast<sync_status*>(buf.data());
+ if (hdr->id == ID_OKAY) {
+ buf.resize(0);
+ if (hdr->msglen != 0) {
+ Error("received ID_OKAY with msg_len (%" PRIu32 " != 0", hdr->msglen);
+ return false;
+ }
+ CopyDone();
+ continue;
+ } else if (hdr->id != ID_FAIL) {
+ Error("unexpected response from daemon: id = %#" PRIx32, hdr->id);
+ return false;
+ } else if (hdr->msglen > SYNC_DATA_MAX) {
+ Error("too-long message length from daemon: msglen = %" PRIu32, hdr->msglen);
+ return false;
+ }
+
+ const ssize_t msg_bytes_left = hdr->msglen + sizeof(sync_status) - buf.size();
+ CHECK_GE(msg_bytes_left, 0);
+ if (msg_bytes_left > 0) {
+ ssize_t rc = adb_read(fd, buf.end(), msg_bytes_left);
+ if (rc <= 0) {
+ Error("failed to read copy failure message");
+ return false;
+ }
+
+ buf.resize(buf.size() + rc);
+ if (rc != msg_bytes_left) {
+ if (should_block) {
+ continue;
+ } else {
+ return true;
+ }
+ }
+
+ std::string msg(buf.begin() + sizeof(sync_status), buf.end());
+ ReportDeferredCopyFailure(msg);
+ buf.resize(0);
+ return false;
+ }
+ }
+
+ return true;
+ }
+
void Printf(const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) {
std::string s;
@@ -616,10 +937,16 @@
size_t max;
private:
- bool expect_done_;
- FeatureSet features_;
+ std::deque<std::pair<std::string, std::string>> deferred_acknowledgements_;
+ Block acknowledgement_buffer_;
+ const FeatureSet* features_ = nullptr;
bool have_stat_v2_;
bool have_ls_v2_;
+ bool have_sendrecv_v2_;
+ bool have_sendrecv_v2_brotli_;
+ bool have_sendrecv_v2_lz4_;
+ bool have_sendrecv_v2_zstd_;
+ bool have_sendrecv_v2_dry_run_send_;
TransferLedger global_ledger_;
TransferLedger current_ledger_;
@@ -629,16 +956,19 @@
return SendRequest(ID_QUIT, ""); // TODO: add a SendResponse?
}
- bool WriteOrDie(const char* from, const char* to, const void* data, size_t data_length) {
+ bool WriteOrDie(const std::string& from, const std::string& to, const void* data,
+ size_t data_length) {
if (!WriteFdExactly(fd, data, data_length)) {
if (errno == ECONNRESET) {
// Assume adbd told us why it was closing the connection, and
// try to read failure reason from adbd.
syncmsg msg;
if (!ReadFdExactly(fd, &msg.status, sizeof(msg.status))) {
- Error("failed to copy '%s' to '%s': no response: %s", from, to, strerror(errno));
+ Error("failed to copy '%s' to '%s': no response: %s", from.c_str(), to.c_str(),
+ strerror(errno));
} else if (msg.status.id != ID_FAIL) {
- Error("failed to copy '%s' to '%s': not ID_FAIL: %d", from, to, msg.status.id);
+ Error("failed to copy '%s' to '%s': not ID_FAIL: %d", from.c_str(), to.c_str(),
+ msg.status.id);
} else {
ReportCopyFailure(from, to, msg);
}
@@ -651,20 +981,20 @@
}
};
-static bool sync_ls(SyncConnection& sc, const char* path,
+static bool sync_ls(SyncConnection& sc, const std::string& path,
const std::function<sync_ls_cb>& func) {
return sc.SendLs(path) && sc.FinishLs(func);
}
-static bool sync_stat(SyncConnection& sc, const char* path, struct stat* st) {
+static bool sync_stat(SyncConnection& sc, const std::string& path, struct stat* st) {
return sc.SendStat(path) && sc.FinishStat(st);
}
-static bool sync_lstat(SyncConnection& sc, const char* path, struct stat* st) {
+static bool sync_lstat(SyncConnection& sc, const std::string& path, struct stat* st) {
return sc.SendLstat(path) && sc.FinishStat(st);
}
-static bool sync_stat_fallback(SyncConnection& sc, const char* path, struct stat* st) {
+static bool sync_stat_fallback(SyncConnection& sc, const std::string& path, struct stat* st) {
if (sync_stat(sc, path, st)) {
return true;
}
@@ -688,7 +1018,7 @@
struct stat tmp_st;
st->st_mode &= ~S_IFMT;
- if (sync_lstat(sc, dir_path.c_str(), &tmp_st)) {
+ if (sync_lstat(sc, dir_path, &tmp_st)) {
st->st_mode |= S_IFDIR;
} else {
st->st_mode |= S_IFREG;
@@ -697,10 +1027,9 @@
return true;
}
-static bool sync_send(SyncConnection& sc, const char* lpath, const char* rpath, unsigned mtime,
- mode_t mode, bool sync) {
- std::string path_and_mode = android::base::StringPrintf("%s,%d", rpath, mode);
-
+static bool sync_send(SyncConnection& sc, const std::string& lpath, const std::string& rpath,
+ unsigned mtime, mode_t mode, bool sync, CompressionType compression,
+ bool dry_run) {
if (sync) {
struct stat st;
if (sync_lstat(sc, rpath, &st)) {
@@ -714,46 +1043,46 @@
if (S_ISLNK(mode)) {
#if !defined(_WIN32)
char buf[PATH_MAX];
- ssize_t data_length = readlink(lpath, buf, PATH_MAX - 1);
+ ssize_t data_length = readlink(lpath.c_str(), buf, PATH_MAX - 1);
if (data_length == -1) {
- sc.Error("readlink '%s' failed: %s", lpath, strerror(errno));
+ sc.Error("readlink '%s' failed: %s", lpath.c_str(), strerror(errno));
return false;
}
buf[data_length++] = '\0';
- if (!sc.SendSmallFile(path_and_mode.c_str(), lpath, rpath, mtime, buf, data_length)) {
+ if (!sc.SendSmallFile(rpath, mode, lpath, rpath, mtime, buf, data_length, dry_run)) {
return false;
}
- return sc.CopyDone(lpath, rpath);
+ return sc.ReadAcknowledgements();
#endif
}
struct stat st;
- if (stat(lpath, &st) == -1) {
- sc.Error("failed to stat local file '%s': %s", lpath, strerror(errno));
+ if (stat(lpath.c_str(), &st) == -1) {
+ sc.Error("failed to stat local file '%s': %s", lpath.c_str(), strerror(errno));
return false;
}
if (st.st_size < SYNC_DATA_MAX) {
std::string data;
if (!android::base::ReadFileToString(lpath, &data, true)) {
- sc.Error("failed to read all of '%s': %s", lpath, strerror(errno));
+ sc.Error("failed to read all of '%s': %s", lpath.c_str(), strerror(errno));
return false;
}
- if (!sc.SendSmallFile(path_and_mode.c_str(), lpath, rpath, mtime,
- data.data(), data.size())) {
+ if (!sc.SendSmallFile(rpath, mode, lpath, rpath, mtime, data.data(), data.size(),
+ dry_run)) {
return false;
}
} else {
- if (!sc.SendLargeFile(path_and_mode.c_str(), lpath, rpath, mtime)) {
+ if (!sc.SendLargeFile(rpath, mode, lpath, rpath, mtime, compression, dry_run)) {
return false;
}
}
- return sc.CopyDone(lpath, rpath);
+ return sc.ReadAcknowledgements();
}
-static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath,
- const char* name, uint64_t expected_size) {
- if (!sc.SendRequest(ID_RECV, rpath)) return false;
+static bool sync_recv_v1(SyncConnection& sc, const char* rpath, const char* lpath, const char* name,
+ uint64_t expected_size) {
+ if (!sc.SendRequest(ID_RECV_V1, rpath)) return false;
adb_unlink(lpath);
unique_fd lfd(adb_creat(lpath, 0644));
@@ -806,6 +1135,124 @@
return true;
}
+static bool sync_recv_v2(SyncConnection& sc, const char* rpath, const char* lpath, const char* name,
+ uint64_t expected_size, CompressionType compression) {
+ compression = sc.ResolveCompressionType(compression);
+
+ if (!sc.SendRecv2(rpath, compression)) return false;
+
+ adb_unlink(lpath);
+ unique_fd lfd(adb_creat(lpath, 0644));
+ if (lfd < 0) {
+ sc.Error("cannot create '%s': %s", lpath, strerror(errno));
+ return false;
+ }
+
+ uint64_t bytes_copied = 0;
+
+ Block buffer(SYNC_DATA_MAX);
+ std::variant<std::monostate, NullDecoder, BrotliDecoder, LZ4Decoder, ZstdDecoder>
+ decoder_storage;
+ Decoder* decoder = nullptr;
+
+ std::span buffer_span(buffer.data(), buffer.size());
+ switch (compression) {
+ case CompressionType::None:
+ decoder = &decoder_storage.emplace<NullDecoder>(buffer_span);
+ break;
+
+ case CompressionType::Brotli:
+ decoder = &decoder_storage.emplace<BrotliDecoder>(buffer_span);
+ break;
+
+ case CompressionType::LZ4:
+ decoder = &decoder_storage.emplace<LZ4Decoder>(buffer_span);
+ break;
+
+ case CompressionType::Zstd:
+ decoder = &decoder_storage.emplace<ZstdDecoder>(buffer_span);
+ break;
+
+ case CompressionType::Any:
+ LOG(FATAL) << "unexpected CompressionType::Any";
+ }
+
+ while (true) {
+ syncmsg msg;
+ if (!ReadFdExactly(sc.fd, &msg.data, sizeof(msg.data))) {
+ adb_unlink(lpath);
+ return false;
+ }
+
+ if (msg.data.id == ID_DONE) {
+ if (!decoder->Finish()) {
+ sc.Error("unexpected ID_DONE");
+ return false;
+ }
+ } else if (msg.data.id != ID_DATA) {
+ adb_unlink(lpath);
+ sc.ReportCopyFailure(rpath, lpath, msg);
+ return false;
+ } else {
+ if (msg.data.size > sc.max) {
+ sc.Error("msg.data.size too large: %u (max %zu)", msg.data.size, sc.max);
+ adb_unlink(lpath);
+ return false;
+ }
+
+ Block block(msg.data.size);
+ if (!ReadFdExactly(sc.fd, block.data(), msg.data.size)) {
+ adb_unlink(lpath);
+ return false;
+ }
+ decoder->Append(std::move(block));
+ }
+
+ while (true) {
+ std::span<char> output;
+ DecodeResult result = decoder->Decode(&output);
+
+ if (result == DecodeResult::Error) {
+ sc.Error("decompress failed");
+ adb_unlink(lpath);
+ return false;
+ }
+
+ if (!output.empty()) {
+ if (!WriteFdExactly(lfd, output.data(), output.size())) {
+ sc.Error("cannot write '%s': %s", lpath, strerror(errno));
+ adb_unlink(lpath);
+ return false;
+ }
+ }
+
+ bytes_copied += output.size();
+ sc.RecordBytesTransferred(output.size());
+ sc.ReportProgress(name != nullptr ? name : rpath, bytes_copied, expected_size);
+
+ if (result == DecodeResult::NeedInput) {
+ break;
+ } else if (result == DecodeResult::MoreOutput) {
+ continue;
+ } else if (result == DecodeResult::Done) {
+ sc.RecordFilesTransferred(1);
+ return true;
+ } else {
+ LOG(FATAL) << "invalid DecodeResult: " << static_cast<int>(result);
+ }
+ }
+ }
+}
+
+static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath, const char* name,
+ uint64_t expected_size, CompressionType compression) {
+ if (sc.HaveSendRecv2()) {
+ return sync_recv_v2(sc, rpath, lpath, name, expected_size, compression);
+ } else {
+ return sync_recv_v1(sc, rpath, lpath, name, expected_size);
+ }
+}
+
bool do_sync_ls(const char* path) {
SyncConnection sc;
if (!sc.IsValid()) return false;
@@ -881,9 +1328,9 @@
return true;
}
-static bool copy_local_dir_remote(SyncConnection& sc, std::string lpath,
- std::string rpath, bool check_timestamps,
- bool list_only) {
+static bool copy_local_dir_remote(SyncConnection& sc, std::string lpath, std::string rpath,
+ bool check_timestamps, bool list_only,
+ CompressionType compression, bool dry_run) {
sc.NewTransfer();
// Make sure that both directory paths end in a slash.
@@ -943,7 +1390,7 @@
if (check_timestamps) {
for (const copyinfo& ci : file_list) {
- if (!sc.SendLstat(ci.rpath.c_str())) {
+ if (!sc.SendLstat(ci.rpath)) {
sc.Error("failed to send lstat");
return false;
}
@@ -965,7 +1412,8 @@
if (list_only) {
sc.Println("would push: %s -> %s", ci.lpath.c_str(), ci.rpath.c_str());
} else {
- if (!sync_send(sc, ci.lpath.c_str(), ci.rpath.c_str(), ci.time, ci.mode, false)) {
+ if (!sync_send(sc, ci.lpath, ci.rpath, ci.time, ci.mode, false, compression,
+ dry_run)) {
return false;
}
}
@@ -975,11 +1423,13 @@
}
sc.RecordFilesSkipped(skipped);
+ bool success = sc.ReadAcknowledgements(true);
sc.ReportTransferRate(lpath, TransferDirection::push);
- return true;
+ return success;
}
-bool do_sync_push(const std::vector<const char*>& srcs, const char* dst, bool sync) {
+bool do_sync_push(const std::vector<const char*>& srcs, const char* dst, bool sync,
+ CompressionType compression, bool dry_run) {
SyncConnection sc;
if (!sc.IsValid()) return false;
@@ -1044,7 +1494,8 @@
dst_dir.append(android::base::Basename(src_path));
}
- success &= copy_local_dir_remote(sc, src_path, dst_dir, sync, false);
+ success &=
+ copy_local_dir_remote(sc, src_path, dst_dir, sync, false, compression, dry_run);
continue;
} else if (!should_push_file(st.st_mode)) {
sc.Warning("skipping special file '%s' (mode = 0o%o)", src_path, st.st_mode);
@@ -1065,10 +1516,12 @@
sc.NewTransfer();
sc.SetExpectedTotalBytes(st.st_size);
- success &= sync_send(sc, src_path, dst_path, st.st_mtime, st.st_mode, sync);
+ success &= sync_send(sc, src_path, dst_path, st.st_mtime, st.st_mode, sync, compression,
+ dry_run);
sc.ReportTransferRate(src_path, TransferDirection::push);
}
+ success &= sc.ReadAcknowledgements(true);
sc.ReportOverallTransferRate(TransferDirection::push);
return success;
}
@@ -1105,14 +1558,14 @@
}
};
- if (!sync_ls(sc, rpath.c_str(), callback)) {
+ if (!sync_ls(sc, rpath, callback)) {
return false;
}
// Check each symlink we found to see whether it's a file or directory.
for (copyinfo& link_ci : linklist) {
struct stat st;
- if (!sync_stat_fallback(sc, link_ci.rpath.c_str(), &st)) {
+ if (!sync_stat_fallback(sc, link_ci.rpath, &st)) {
sc.Warning("stat failed for path %s: %s", link_ci.rpath.c_str(), strerror(errno));
continue;
}
@@ -1149,8 +1602,8 @@
return r1 ? r1 : r2;
}
-static bool copy_remote_dir_local(SyncConnection& sc, std::string rpath,
- std::string lpath, bool copy_attrs) {
+static bool copy_remote_dir_local(SyncConnection& sc, std::string rpath, std::string lpath,
+ bool copy_attrs, CompressionType compression) {
sc.NewTransfer();
// Make sure that both directory paths end in a slash.
@@ -1180,7 +1633,7 @@
continue;
}
- if (!sync_recv(sc, ci.rpath.c_str(), ci.lpath.c_str(), nullptr, ci.size)) {
+ if (!sync_recv(sc, ci.rpath.c_str(), ci.lpath.c_str(), nullptr, ci.size, compression)) {
return false;
}
@@ -1197,8 +1650,8 @@
return true;
}
-bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst,
- bool copy_attrs, const char* name) {
+bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs,
+ CompressionType compression, const char* name) {
SyncConnection sc;
if (!sc.IsValid()) return false;
@@ -1272,7 +1725,7 @@
dst_dir.append(android::base::Basename(src_path));
}
- success &= copy_remote_dir_local(sc, src_path, dst_dir, copy_attrs);
+ success &= copy_remote_dir_local(sc, src_path, dst_dir, copy_attrs, compression);
continue;
} else if (!should_pull_file(src_st.st_mode)) {
sc.Warning("skipping special file '%s' (mode = 0o%o)", src_path, src_st.st_mode);
@@ -1291,7 +1744,7 @@
sc.NewTransfer();
sc.SetExpectedTotalBytes(src_st.st_size);
- if (!sync_recv(sc, src_path, dst_path, name, src_st.st_size)) {
+ if (!sync_recv(sc, src_path, dst_path, name, src_st.st_size, compression)) {
success = false;
continue;
}
@@ -1307,11 +1760,12 @@
return success;
}
-bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only) {
+bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only,
+ CompressionType compression, bool dry_run) {
SyncConnection sc;
if (!sc.IsValid()) return false;
- bool success = copy_local_dir_remote(sc, lpath, rpath, true, list_only);
+ bool success = copy_local_dir_remote(sc, lpath, rpath, true, list_only, compression, dry_run);
if (!list_only) {
sc.ReportOverallTransferRate(TransferDirection::push);
}
diff --git a/adb/client/file_sync_client.h b/adb/client/file_sync_client.h
index df7f14c..cb8ca93 100644
--- a/adb/client/file_sync_client.h
+++ b/adb/client/file_sync_client.h
@@ -19,9 +19,13 @@
#include <string>
#include <vector>
-bool do_sync_ls(const char* path);
-bool do_sync_push(const std::vector<const char*>& srcs, const char* dst, bool sync);
-bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs,
- const char* name = nullptr);
+#include "file_sync_protocol.h"
-bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only);
+bool do_sync_ls(const char* path);
+bool do_sync_push(const std::vector<const char*>& srcs, const char* dst, bool sync,
+ CompressionType compression, bool dry_run);
+bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs,
+ CompressionType compression, const char* name = nullptr);
+
+bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only,
+ CompressionType compression, bool dry_run);
diff --git a/adb/client/incremental.cpp b/adb/client/incremental.cpp
index 6499d46..3033059 100644
--- a/adb/client/incremental.cpp
+++ b/adb/client/incremental.cpp
@@ -16,13 +16,13 @@
#include "incremental.h"
-#include <android-base/endian.h>
+#include "incremental_utils.h"
+
#include <android-base/file.h>
#include <android-base/stringprintf.h>
#include <openssl/base64.h>
#include "adb_client.h"
-#include "adb_io.h"
#include "adb_utils.h"
#include "commandline.h"
#include "sysdeps.h"
@@ -31,99 +31,73 @@
namespace incremental {
-namespace {
-
-static constexpr auto IDSIG = ".idsig"sv;
-
using android::base::StringPrintf;
-using Size = int64_t;
-
-static inline int32_t read_int32(borrowed_fd fd) {
- int32_t result;
- ReadFully(fd, &result, sizeof(result));
- return result;
-}
-
-static inline int32_t read_be_int32(borrowed_fd fd) {
- return int32_t(be32toh(read_int32(fd)));
-}
-
-static inline void append_int(borrowed_fd fd, std::vector<char>* bytes) {
- int32_t be_val = read_int32(fd);
- auto old_size = bytes->size();
- bytes->resize(old_size + sizeof(be_val));
- memcpy(bytes->data() + old_size, &be_val, sizeof(be_val));
-}
-
-static inline void append_bytes_with_size(borrowed_fd fd, std::vector<char>* bytes) {
- int32_t be_size = read_int32(fd);
- int32_t size = int32_t(be32toh(be_size));
- auto old_size = bytes->size();
- bytes->resize(old_size + sizeof(be_size) + size);
- memcpy(bytes->data() + old_size, &be_size, sizeof(be_size));
- ReadFully(fd, bytes->data() + old_size + sizeof(be_size), size);
-}
-
-static inline std::pair<std::vector<char>, int32_t> read_id_sig_headers(borrowed_fd fd) {
- std::vector<char> result;
- append_int(fd, &result); // version
- append_bytes_with_size(fd, &result); // verityRootHash
- append_bytes_with_size(fd, &result); // v3Digest
- append_bytes_with_size(fd, &result); // pkcs7SignatureBlock
- auto tree_size = read_be_int32(fd); // size of the verity tree
- return {std::move(result), tree_size};
-}
-
-static inline Size verity_tree_size_for_file(Size fileSize) {
- constexpr int INCFS_DATA_FILE_BLOCK_SIZE = 4096;
- constexpr int SHA256_DIGEST_SIZE = 32;
- constexpr int digest_size = SHA256_DIGEST_SIZE;
- constexpr int hash_per_block = INCFS_DATA_FILE_BLOCK_SIZE / digest_size;
-
- Size total_tree_block_count = 0;
-
- auto block_count = 1 + (fileSize - 1) / INCFS_DATA_FILE_BLOCK_SIZE;
- auto hash_block_count = block_count;
- for (auto i = 0; hash_block_count > 1; i++) {
- hash_block_count = (hash_block_count + hash_per_block - 1) / hash_per_block;
- total_tree_block_count += hash_block_count;
- }
- return total_tree_block_count * INCFS_DATA_FILE_BLOCK_SIZE;
-}
-
-// Base64-encode signature bytes. Keeping fd at the position of start of verity tree.
-static std::pair<unique_fd, std::string> read_and_encode_signature(Size file_size,
- std::string signature_file) {
+// Read, verify and return the signature bytes. Keeping fd at the position of start of verity tree.
+static std::pair<unique_fd, std::vector<char>> read_signature(Size file_size,
+ std::string signature_file,
+ bool silent) {
signature_file += IDSIG;
struct stat st;
if (stat(signature_file.c_str(), &st)) {
- fprintf(stderr, "Failed to stat signature file %s. Abort.\n", signature_file.c_str());
+ if (!silent) {
+ fprintf(stderr, "Failed to stat signature file %s.\n", signature_file.c_str());
+ }
return {};
}
- unique_fd fd(adb_open(signature_file.c_str(), O_RDONLY | O_CLOEXEC));
+ unique_fd fd(adb_open(signature_file.c_str(), O_RDONLY));
if (fd < 0) {
- fprintf(stderr, "Failed to open signature file: %s. Abort.\n", signature_file.c_str());
+ if (!silent) {
+ fprintf(stderr, "Failed to open signature file: %s.\n", signature_file.c_str());
+ }
return {};
}
+ std::vector<char> invalid_signature;
+
+ if (st.st_size > kMaxSignatureSize) {
+ if (!silent) {
+ fprintf(stderr, "Signature is too long. Max allowed is %d. Abort.\n",
+ kMaxSignatureSize);
+ }
+ return {std::move(fd), std::move(invalid_signature)};
+ }
+
auto [signature, tree_size] = read_id_sig_headers(fd);
if (auto expected = verity_tree_size_for_file(file_size); tree_size != expected) {
- fprintf(stderr,
- "Verity tree size mismatch in signature file: %s [was %lld, expected %lld].\n",
- signature_file.c_str(), (long long)tree_size, (long long)expected);
- return {};
+ if (!silent) {
+ fprintf(stderr,
+ "Verity tree size mismatch in signature file: %s [was %lld, expected %lld].\n",
+ signature_file.c_str(), (long long)tree_size, (long long)expected);
+ }
+ return {std::move(fd), std::move(invalid_signature)};
+ }
+
+ return {std::move(fd), std::move(signature)};
+}
+
+// Base64-encode signature bytes. Keeping fd at the position of start of verity tree.
+static std::pair<unique_fd, std::string> read_and_encode_signature(Size file_size,
+ std::string signature_file,
+ bool silent) {
+ std::string encoded_signature;
+
+ auto [fd, signature] = read_signature(file_size, std::move(signature_file), silent);
+ if (!fd.ok() || signature.empty()) {
+ return {std::move(fd), std::move(encoded_signature)};
}
size_t base64_len = 0;
if (!EVP_EncodedLength(&base64_len, signature.size())) {
- fprintf(stderr, "Fail to estimate base64 encoded length. Abort.\n");
- return {};
+ if (!silent) {
+ fprintf(stderr, "Fail to estimate base64 encoded length. Abort.\n");
+ }
+ return {std::move(fd), std::move(encoded_signature)};
}
- std::string encoded_signature;
- encoded_signature.resize(base64_len);
+
+ encoded_signature.resize(base64_len, '\0');
encoded_signature.resize(EVP_EncodeBlock((uint8_t*)encoded_signature.data(),
(const uint8_t*)signature.data(), signature.size()));
@@ -132,85 +106,151 @@
// Send install-incremental to the device along with properly configured file descriptors in
// streaming format. Once connection established, send all fs-verity tree bytes.
-static unique_fd start_install(const std::vector<std::string>& files) {
+static unique_fd start_install(const Files& files, const Args& passthrough_args, bool silent) {
std::vector<std::string> command_args{"package", "install-incremental"};
+ command_args.insert(command_args.end(), passthrough_args.begin(), passthrough_args.end());
- // fd's with positions at the beginning of fs-verity
- std::vector<unique_fd> signature_fds;
- signature_fds.reserve(files.size());
for (int i = 0, size = files.size(); i < size; ++i) {
const auto& file = files[i];
struct stat st;
if (stat(file.c_str(), &st)) {
- fprintf(stderr, "Failed to stat input file %s. Abort.\n", file.c_str());
+ if (!silent) {
+ fprintf(stderr, "Failed to stat input file %s. Abort.\n", file.c_str());
+ }
return {};
}
- auto [signature_fd, signature] = read_and_encode_signature(st.st_size, file);
- if (!signature_fd.ok()) {
+ auto [signature_fd, signature] = read_and_encode_signature(st.st_size, file, silent);
+ if (signature_fd.ok() && signature.empty()) {
return {};
}
- auto file_desc =
- StringPrintf("%s:%lld:%s:%s", android::base::Basename(file).c_str(),
- (long long)st.st_size, std::to_string(i).c_str(), signature.c_str());
+ auto file_desc = StringPrintf("%s:%lld:%d:%s:1", android::base::Basename(file).c_str(),
+ (long long)st.st_size, i, signature.c_str());
command_args.push_back(std::move(file_desc));
-
- signature_fds.push_back(std::move(signature_fd));
}
std::string error;
auto connection_fd = unique_fd(send_abb_exec_command(command_args, &error));
if (connection_fd < 0) {
- fprintf(stderr, "Failed to run: %s, error: %s\n",
- android::base::Join(command_args, " ").c_str(), error.c_str());
- return {};
- }
-
- // Pushing verity trees for all installation files.
- for (auto&& local_fd : signature_fds) {
- if (!copy_to_file(local_fd.get(), connection_fd.get())) {
- fprintf(stderr, "Failed to stream tree bytes: %s. Abort.\n", strerror(errno));
- return {};
+ if (!silent) {
+ fprintf(stderr, "Failed to run: %s, error: %s\n",
+ android::base::Join(command_args, " ").c_str(), error.c_str());
}
+ return {};
}
return connection_fd;
}
-} // namespace
+bool can_install(const Files& files) {
+ for (const auto& file : files) {
+ struct stat st;
+ if (stat(file.c_str(), &st)) {
+ return false;
+ }
-std::optional<Process> install(std::vector<std::string> files) {
- auto connection_fd = start_install(files);
+ if (android::base::EndsWithIgnoreCase(file, ".apk")) {
+ // Signature has to be present for APKs.
+ auto [fd, _] = read_signature(st.st_size, file, /*silent=*/true);
+ if (!fd.ok()) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+std::optional<Process> install(const Files& files, const Args& passthrough_args, bool silent) {
+ auto connection_fd = start_install(files, passthrough_args, silent);
if (connection_fd < 0) {
- fprintf(stderr, "adb: failed to initiate installation on device.\n");
+ if (!silent) {
+ fprintf(stderr, "adb: failed to initiate installation on device.\n");
+ }
return {};
}
std::string adb_path = android::base::GetExecutablePath();
- auto osh = adb_get_os_handle(connection_fd.get());
-#ifdef _WIN32
- auto fd_param = std::to_string(reinterpret_cast<intptr_t>(osh));
-#else /* !_WIN32 a.k.a. Unix */
+ auto osh = cast_handle_to_int(adb_get_os_handle(connection_fd.get()));
auto fd_param = std::to_string(osh);
-#endif
+
+ // pipe for child process to write output
+ int print_fds[2];
+ if (adb_socketpair(print_fds) != 0) {
+ if (!silent) {
+ fprintf(stderr, "adb: failed to create socket pair for child to print to parent\n");
+ }
+ return {};
+ }
+ auto [pipe_read_fd, pipe_write_fd] = print_fds;
+ auto pipe_write_fd_param = std::to_string(cast_handle_to_int(adb_get_os_handle(pipe_write_fd)));
+ close_on_exec(pipe_read_fd);
std::vector<std::string> args(std::move(files));
- args.insert(args.begin(), {"inc-server", fd_param});
- auto child = adb_launch_process(adb_path, std::move(args), {connection_fd.get()});
+ args.insert(args.begin(), {"inc-server", fd_param, pipe_write_fd_param});
+ auto child =
+ adb_launch_process(adb_path, std::move(args), {connection_fd.get(), pipe_write_fd});
if (!child) {
- fprintf(stderr, "adb: failed to fork: %s\n", strerror(errno));
+ if (!silent) {
+ fprintf(stderr, "adb: failed to fork: %s\n", strerror(errno));
+ }
return {};
}
+ adb_close(pipe_write_fd);
+
auto killOnExit = [](Process* p) { p->kill(); };
std::unique_ptr<Process, decltype(killOnExit)> serverKiller(&child, killOnExit);
- // TODO: Terminate server process if installation fails.
- serverKiller.release();
+ Result result = wait_for_installation(pipe_read_fd);
+ adb_close(pipe_read_fd);
+
+ if (result != Result::Success) {
+ if (!silent) {
+ fprintf(stderr, "adb: install command failed");
+ }
+ return {};
+ }
+
+ // adb client exits now but inc-server can continue
+ serverKiller.release();
return child;
}
+Result wait_for_installation(int read_fd) {
+ static constexpr int maxMessageSize = 256;
+ std::vector<char> child_stdout(CHUNK_SIZE);
+ int bytes_read;
+ int buf_size = 0;
+ // TODO(b/150865433): optimize child's output parsing
+ while ((bytes_read = adb_read(read_fd, child_stdout.data() + buf_size,
+ child_stdout.size() - buf_size)) > 0) {
+ // print to parent's stdout
+ fprintf(stdout, "%.*s", bytes_read, child_stdout.data() + buf_size);
+
+ buf_size += bytes_read;
+ const std::string_view stdout_str(child_stdout.data(), buf_size);
+ // wait till installation either succeeds or fails
+ if (stdout_str.find("Success") != std::string::npos) {
+ return Result::Success;
+ }
+ // on failure, wait for full message
+ static constexpr auto failure_msg_head = "Failure ["sv;
+ if (const auto begin_itr = stdout_str.find(failure_msg_head);
+ begin_itr != std::string::npos) {
+ if (buf_size >= maxMessageSize) {
+ return Result::Failure;
+ }
+ const auto end_itr = stdout_str.rfind("]");
+ if (end_itr != std::string::npos && end_itr >= begin_itr + failure_msg_head.size()) {
+ return Result::Failure;
+ }
+ }
+ child_stdout.resize(buf_size + CHUNK_SIZE);
+ }
+ return Result::None;
+}
+
} // namespace incremental
diff --git a/adb/client/incremental.h b/adb/client/incremental.h
index 4b9f6bd..40e928a 100644
--- a/adb/client/incremental.h
+++ b/adb/client/incremental.h
@@ -25,6 +25,13 @@
namespace incremental {
-std::optional<Process> install(std::vector<std::string> files);
+using Files = std::vector<std::string>;
+using Args = std::vector<std::string_view>;
+
+bool can_install(const Files& files);
+std::optional<Process> install(const Files& files, const Args& passthrough_args, bool silent);
+
+enum class Result { Success, Failure, None };
+Result wait_for_installation(int read_fd);
} // namespace incremental
diff --git a/adb/client/incremental_server.cpp b/adb/client/incremental_server.cpp
index 2512d05..0654a11 100644
--- a/adb/client/incremental_server.cpp
+++ b/adb/client/incremental_server.cpp
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,13 +18,6 @@
#include "incremental_server.h"
-#include "adb.h"
-#include "adb_io.h"
-#include "adb_trace.h"
-#include "adb_unique_fd.h"
-#include "adb_utils.h"
-#include "sysdeps.h"
-
#include <android-base/endian.h>
#include <android-base/strings.h>
#include <inttypes.h>
@@ -41,29 +34,42 @@
#include <type_traits>
#include <unordered_set>
+#include "adb.h"
+#include "adb_io.h"
+#include "adb_trace.h"
+#include "adb_unique_fd.h"
+#include "adb_utils.h"
+#include "incremental_utils.h"
+#include "sysdeps.h"
+
namespace incremental {
-static constexpr int kBlockSize = 4096;
+static constexpr int kHashesPerBlock = kBlockSize / kDigestSize;
static constexpr int kCompressedSizeMax = kBlockSize * 0.95;
-static constexpr short kCompressionNone = 0;
-static constexpr short kCompressionLZ4 = 1;
+static constexpr int8_t kTypeData = 0;
+static constexpr int8_t kTypeHash = 1;
+static constexpr int8_t kCompressionNone = 0;
+static constexpr int8_t kCompressionLZ4 = 1;
static constexpr int kCompressBound = std::max(kBlockSize, LZ4_COMPRESSBOUND(kBlockSize));
static constexpr auto kReadBufferSize = 128 * 1024;
+static constexpr int kPollTimeoutMillis = 300000; // 5 minutes
using BlockSize = int16_t;
using FileId = int16_t;
using BlockIdx = int32_t;
using NumBlocks = int32_t;
-using CompressionType = int16_t;
+using BlockType = int8_t;
+using CompressionType = int8_t;
using RequestType = int16_t;
using ChunkHeader = int32_t;
using MagicType = uint32_t;
static constexpr MagicType INCR = 0x494e4352; // LE INCR
-static constexpr RequestType EXIT = 0;
+static constexpr RequestType SERVING_COMPLETE = 0;
static constexpr RequestType BLOCK_MISSING = 1;
static constexpr RequestType PREFETCH = 2;
+static constexpr RequestType DESTROY = 3;
static constexpr inline int64_t roundDownToBlockOffset(int64_t val) {
return val & ~(kBlockSize - 1);
@@ -123,48 +129,79 @@
// Placed before actual data bytes of each block
struct ResponseHeader {
FileId file_id; // 2 bytes
- CompressionType compression_type; // 2 bytes
+ BlockType block_type; // 1 byte
+ CompressionType compression_type; // 1 byte
BlockIdx block_idx; // 4 bytes
BlockSize block_size; // 2 bytes
+
+ static constexpr size_t responseSizeFor(size_t dataSize) {
+ return dataSize + sizeof(ResponseHeader);
+ }
+} __attribute__((packed));
+
+template <size_t Size = kBlockSize>
+struct BlockBuffer {
+ ResponseHeader header;
+ char data[Size];
} __attribute__((packed));
// Holds streaming state for a file
class File {
public:
// Plain file
- File(const char* filepath, FileId id, int64_t size, unique_fd fd) : File(filepath, id, size) {
+ File(const char* filepath, FileId id, int64_t size, unique_fd fd, int64_t tree_offset,
+ unique_fd tree_fd)
+ : File(filepath, id, size, tree_offset) {
this->fd_ = std::move(fd);
+ this->tree_fd_ = std::move(tree_fd);
+ priority_blocks_ = PriorityBlocksForFile(filepath, fd_.get(), size);
}
- int64_t ReadBlock(BlockIdx block_idx, void* buf, bool* is_zip_compressed,
- std::string* error) const {
- char* buf_ptr = static_cast<char*>(buf);
+ int64_t ReadDataBlock(BlockIdx block_idx, void* buf, bool* is_zip_compressed) const {
int64_t bytes_read = -1;
const off64_t offsetStart = blockIndexToOffset(block_idx);
- bytes_read = adb_pread(fd_, &buf_ptr[sizeof(ResponseHeader)], kBlockSize, offsetStart);
+ bytes_read = adb_pread(fd_, buf, kBlockSize, offsetStart);
+ return bytes_read;
+ }
+ int64_t ReadTreeBlock(BlockIdx block_idx, void* buf) const {
+ int64_t bytes_read = -1;
+ const off64_t offsetStart = tree_offset_ + blockIndexToOffset(block_idx);
+ bytes_read = adb_pread(tree_fd_, buf, kBlockSize, offsetStart);
return bytes_read;
}
- const unique_fd& RawFd() const { return fd_; }
+ const std::vector<BlockIdx>& PriorityBlocks() const { return priority_blocks_; }
+
+ bool hasTree() const { return tree_fd_.ok(); }
std::vector<bool> sentBlocks;
NumBlocks sentBlocksCount = 0;
+ std::vector<bool> sentTreeBlocks;
+
const char* const filepath;
const FileId id;
const int64_t size;
private:
- File(const char* filepath, FileId id, int64_t size) : filepath(filepath), id(id), size(size) {
+ File(const char* filepath, FileId id, int64_t size, int64_t tree_offset)
+ : filepath(filepath), id(id), size(size), tree_offset_(tree_offset) {
sentBlocks.resize(numBytesToNumBlocks(size));
+ sentTreeBlocks.resize(verity_tree_blocks_for_file(size));
}
unique_fd fd_;
+ std::vector<BlockIdx> priority_blocks_;
+
+ unique_fd tree_fd_;
+ const int64_t tree_offset_;
};
class IncrementalServer {
public:
- IncrementalServer(unique_fd fd, std::vector<File> files)
- : adb_fd_(std::move(fd)), files_(std::move(files)) {
+ IncrementalServer(unique_fd adb_fd, unique_fd output_fd, std::vector<File> files)
+ : adb_fd_(std::move(adb_fd)), output_fd_(std::move(output_fd)), files_(std::move(files)) {
buffer_.reserve(kReadBufferSize);
+ pendingBlocksBuffer_.resize(kChunkFlushSize + 2 * kBlockSize);
+ pendingBlocks_ = pendingBlocksBuffer_.data() + sizeof(ChunkHeader);
}
bool Serve();
@@ -174,14 +211,23 @@
const File* file;
BlockIdx overallIndex = 0;
BlockIdx overallEnd = 0;
+ BlockIdx priorityIndex = 0;
- PrefetchState(const File& f) : file(&f), overallEnd((BlockIdx)f.sentBlocks.size()) {}
- PrefetchState(const File& f, BlockIdx start, int count)
+ explicit PrefetchState(const File& f, BlockIdx start, int count)
: file(&f),
overallIndex(start),
overallEnd(std::min<BlockIdx>(start + count, f.sentBlocks.size())) {}
- bool done() const { return overallIndex >= overallEnd; }
+ explicit PrefetchState(const File& f)
+ : PrefetchState(f, 0, (BlockIdx)f.sentBlocks.size()) {}
+
+ bool done() const {
+ const bool overallSent = (overallIndex >= overallEnd);
+ if (file->PriorityBlocks().empty()) {
+ return overallSent;
+ }
+ return overallSent && (priorityIndex >= (BlockIdx)file->PriorityBlocks().size());
+ }
};
bool SkipToRequest(void* buffer, size_t* size, bool blocking);
@@ -190,16 +236,21 @@
void erase_buffer_head(int count) { buffer_.erase(buffer_.begin(), buffer_.begin() + count); }
enum class SendResult { Sent, Skipped, Error };
- SendResult SendBlock(FileId fileId, BlockIdx blockIdx, bool flush = false);
+ SendResult SendDataBlock(FileId fileId, BlockIdx blockIdx, bool flush = false);
+
+ bool SendTreeBlock(FileId fileId, int32_t fileBlockIdx, BlockIdx blockIdx);
+ bool SendTreeBlocksForDataBlock(FileId fileId, BlockIdx blockIdx);
+
bool SendDone();
void RunPrefetching();
void Send(const void* data, size_t size, bool flush);
void Flush();
using TimePoint = decltype(std::chrono::high_resolution_clock::now());
- bool Exit(std::optional<TimePoint> startTime, int missesCount, int missesSent);
+ bool ServingComplete(std::optional<TimePoint> startTime, int missesCount, int missesSent);
unique_fd const adb_fd_;
+ unique_fd const output_fd_;
std::vector<File> files_;
// Incoming data buffer.
@@ -209,7 +260,13 @@
int compressed_ = 0, uncompressed_ = 0;
long long sentSize_ = 0;
- std::vector<char> pendingBlocks_;
+ static constexpr auto kChunkFlushSize = 31 * kBlockSize;
+
+ std::vector<char> pendingBlocksBuffer_;
+ char* pendingBlocks_ = nullptr;
+
+ // True when client notifies that all the data has been received
+ bool servingComplete_ = false;
};
bool IncrementalServer::SkipToRequest(void* buffer, size_t* size, bool blocking) {
@@ -217,7 +274,8 @@
// Looking for INCR magic.
bool magic_found = false;
int bcur = 0;
- for (int bsize = buffer_.size(); bcur + 4 < bsize; ++bcur) {
+ int bsize = buffer_.size();
+ for (bcur = 0; bcur + 4 < bsize; ++bcur) {
uint32_t magic = be32toh(*(uint32_t*)(buffer_.data() + bcur));
if (magic == INCR) {
magic_found = true;
@@ -226,8 +284,8 @@
}
if (bcur > 0) {
- // Stream the rest to stderr.
- fprintf(stderr, "%.*s", bcur, buffer_.data());
+ // output the rest.
+ (void)WriteFdExactly(output_fd_, buffer_.data(), bcur);
erase_buffer_head(bcur);
}
@@ -239,17 +297,27 @@
}
adb_pollfd pfd = {adb_fd_.get(), POLLIN, 0};
- auto res = adb_poll(&pfd, 1, blocking ? -1 : 0);
+ auto res = adb_poll(&pfd, 1, blocking ? kPollTimeoutMillis : 0);
+
if (res != 1) {
+ auto err = errno;
+ (void)WriteFdExactly(output_fd_, buffer_.data(), buffer_.size());
if (res < 0) {
- fprintf(stderr, "Failed to poll: %s\n", strerror(errno));
+ D("Failed to poll: %s", strerror(err));
+ return false;
+ }
+ if (blocking) {
+ fprintf(stderr, "Timed out waiting for data from device.\n");
+ }
+ if (blocking && servingComplete_) {
+ // timeout waiting from client. Serving is complete, so quit.
return false;
}
*size = 0;
return true;
}
- auto bsize = buffer_.size();
+ bsize = buffer_.size();
buffer_.resize(kReadBufferSize);
int r = adb_read(adb_fd_, buffer_.data() + bsize, kReadBufferSize - bsize);
if (r > 0) {
@@ -257,21 +325,19 @@
continue;
}
- if (r == -1) {
- fprintf(stderr, "Failed to read from fd %d: %d. Exit\n", adb_fd_.get(), errno);
- return false;
- }
-
- // socket is closed
- return false;
+ D("Failed to read from fd %d: %d. Exit", adb_fd_.get(), errno);
+ break;
}
+ // socket is closed. print remaining messages
+ WriteFdExactly(output_fd_, buffer_.data(), buffer_.size());
+ return false;
}
std::optional<RequestCommand> IncrementalServer::ReadRequest(bool blocking) {
uint8_t commandBuf[sizeof(RequestCommand)];
auto size = sizeof(commandBuf);
if (!SkipToRequest(&commandBuf, &size, blocking)) {
- return {{EXIT}};
+ return {{DESTROY}};
}
if (size < sizeof(RequestCommand)) {
return {};
@@ -283,60 +349,123 @@
return request;
}
-auto IncrementalServer::SendBlock(FileId fileId, BlockIdx blockIdx, bool flush) -> SendResult {
+bool IncrementalServer::SendTreeBlocksForDataBlock(const FileId fileId, const BlockIdx blockIdx) {
+ auto& file = files_[fileId];
+ if (!file.hasTree()) {
+ return true;
+ }
+ const int32_t data_block_count = numBytesToNumBlocks(file.size);
+
+ const int32_t total_nodes_count(file.sentTreeBlocks.size());
+ const int32_t leaf_nodes_count = (data_block_count + kHashesPerBlock - 1) / kHashesPerBlock;
+
+ const int32_t leaf_nodes_offset = total_nodes_count - leaf_nodes_count;
+
+ // Leaf level, sending only 1 block.
+ const int32_t leaf_idx = leaf_nodes_offset + blockIdx / kHashesPerBlock;
+ if (file.sentTreeBlocks[leaf_idx]) {
+ return true;
+ }
+ if (!SendTreeBlock(fileId, blockIdx, leaf_idx)) {
+ return false;
+ }
+ file.sentTreeBlocks[leaf_idx] = true;
+
+ // Non-leaf, sending EVERYTHING. This should be done only once.
+ if (leaf_nodes_offset == 0 || file.sentTreeBlocks[0]) {
+ return true;
+ }
+
+ for (int32_t i = 0; i < leaf_nodes_offset; ++i) {
+ if (!SendTreeBlock(fileId, blockIdx, i)) {
+ return false;
+ }
+ file.sentTreeBlocks[i] = true;
+ }
+ return true;
+}
+
+bool IncrementalServer::SendTreeBlock(FileId fileId, int32_t fileBlockIdx, BlockIdx blockIdx) {
+ const auto& file = files_[fileId];
+
+ BlockBuffer buffer;
+ const int64_t bytesRead = file.ReadTreeBlock(blockIdx, buffer.data);
+ if (bytesRead <= 0) {
+ fprintf(stderr, "Failed to get data for %s.idsig at blockIdx=%d.\n", file.filepath,
+ blockIdx);
+ return false;
+ }
+
+ buffer.header.compression_type = kCompressionNone;
+ buffer.header.block_type = kTypeHash;
+ buffer.header.file_id = toBigEndian(fileId);
+ buffer.header.block_size = toBigEndian(int16_t(bytesRead));
+ buffer.header.block_idx = toBigEndian(blockIdx);
+
+ Send(&buffer, ResponseHeader::responseSizeFor(bytesRead), /*flush=*/false);
+
+ return true;
+}
+
+auto IncrementalServer::SendDataBlock(FileId fileId, BlockIdx blockIdx, bool flush) -> SendResult {
auto& file = files_[fileId];
if (blockIdx >= static_cast<long>(file.sentBlocks.size())) {
- fprintf(stderr, "Failed to read file %s at block %" PRId32 " (past end).\n", file.filepath,
- blockIdx);
- return SendResult::Error;
+ // may happen as we schedule some extra blocks for reported page misses
+ D("Skipped reading file %s at block %" PRId32 " (past end).", file.filepath, blockIdx);
+ return SendResult::Skipped;
}
if (file.sentBlocks[blockIdx]) {
return SendResult::Skipped;
}
- std::string error;
- char raw[sizeof(ResponseHeader) + kBlockSize];
- bool isZipCompressed = false;
- const int64_t bytesRead = file.ReadBlock(blockIdx, &raw, &isZipCompressed, &error);
- if (bytesRead < 0) {
- fprintf(stderr, "Failed to get data for %s at blockIdx=%d (%s).\n", file.filepath, blockIdx,
- error.c_str());
+
+ if (!SendTreeBlocksForDataBlock(fileId, blockIdx)) {
return SendResult::Error;
}
- ResponseHeader* header = nullptr;
- char data[sizeof(ResponseHeader) + kCompressBound];
- char* compressed = data + sizeof(*header);
+ BlockBuffer raw;
+ bool isZipCompressed = false;
+ const int64_t bytesRead = file.ReadDataBlock(blockIdx, raw.data, &isZipCompressed);
+ if (bytesRead < 0) {
+ fprintf(stderr, "Failed to get data for %s at blockIdx=%d (%d).\n", file.filepath, blockIdx,
+ errno);
+ return SendResult::Error;
+ }
+
+ BlockBuffer<kCompressBound> compressed;
int16_t compressedSize = 0;
if (!isZipCompressed) {
- compressedSize =
- LZ4_compress_default(raw + sizeof(*header), compressed, bytesRead, kCompressBound);
+ compressedSize = LZ4_compress_default(raw.data, compressed.data, bytesRead, kCompressBound);
}
int16_t blockSize;
+ ResponseHeader* header;
if (compressedSize > 0 && compressedSize < kCompressedSizeMax) {
++compressed_;
blockSize = compressedSize;
- header = reinterpret_cast<ResponseHeader*>(data);
- header->compression_type = toBigEndian(kCompressionLZ4);
+ header = &compressed.header;
+ header->compression_type = kCompressionLZ4;
} else {
++uncompressed_;
blockSize = bytesRead;
- header = reinterpret_cast<ResponseHeader*>(raw);
- header->compression_type = toBigEndian(kCompressionNone);
+ header = &raw.header;
+ header->compression_type = kCompressionNone;
}
+ header->block_type = kTypeData;
header->file_id = toBigEndian(fileId);
header->block_size = toBigEndian(blockSize);
header->block_idx = toBigEndian(blockIdx);
file.sentBlocks[blockIdx] = true;
file.sentBlocksCount += 1;
- Send(header, sizeof(*header) + blockSize, flush);
+ Send(header, ResponseHeader::responseSizeFor(blockSize), flush);
+
return SendResult::Sent;
}
bool IncrementalServer::SendDone() {
ResponseHeader header;
header.file_id = -1;
+ header.block_type = 0;
header.compression_type = 0;
header.block_idx = 0;
header.block_size = 0;
@@ -351,8 +480,20 @@
while (!prefetches_.empty() && blocksToSend > 0) {
auto& prefetch = prefetches_.front();
const auto& file = *prefetch.file;
+ const auto& priority_blocks = file.PriorityBlocks();
+ if (!priority_blocks.empty()) {
+ for (auto& i = prefetch.priorityIndex;
+ blocksToSend > 0 && i < (BlockIdx)priority_blocks.size(); ++i) {
+ if (auto res = SendDataBlock(file.id, priority_blocks[i]);
+ res == SendResult::Sent) {
+ --blocksToSend;
+ } else if (res == SendResult::Error) {
+ fprintf(stderr, "Failed to send priority block %" PRId32 "\n", i);
+ }
+ }
+ }
for (auto& i = prefetch.overallIndex; blocksToSend > 0 && i < prefetch.overallEnd; ++i) {
- if (auto res = SendBlock(file.id, i); res == SendResult::Sent) {
+ if (auto res = SendDataBlock(file.id, i); res == SendResult::Sent) {
--blocksToSend;
} else if (res == SendResult::Error) {
fprintf(stderr, "Failed to send block %" PRId32 "\n", i);
@@ -365,43 +506,38 @@
}
void IncrementalServer::Send(const void* data, size_t size, bool flush) {
- constexpr auto kChunkFlushSize = 31 * kBlockSize;
-
- if (pendingBlocks_.empty()) {
- pendingBlocks_.resize(sizeof(ChunkHeader));
- }
- pendingBlocks_.insert(pendingBlocks_.end(), static_cast<const char*>(data),
- static_cast<const char*>(data) + size);
- if (flush || pendingBlocks_.size() > kChunkFlushSize) {
+ pendingBlocks_ = std::copy_n(static_cast<const char*>(data), size, pendingBlocks_);
+ if (flush || pendingBlocks_ - pendingBlocksBuffer_.data() > kChunkFlushSize) {
Flush();
}
}
void IncrementalServer::Flush() {
- if (pendingBlocks_.empty()) {
+ auto dataBytes = pendingBlocks_ - (pendingBlocksBuffer_.data() + sizeof(ChunkHeader));
+ if (dataBytes == 0) {
return;
}
- *(ChunkHeader*)pendingBlocks_.data() =
- toBigEndian<int32_t>(pendingBlocks_.size() - sizeof(ChunkHeader));
- if (!WriteFdExactly(adb_fd_, pendingBlocks_.data(), pendingBlocks_.size())) {
- fprintf(stderr, "Failed to write %d bytes\n", int(pendingBlocks_.size()));
+ *(ChunkHeader*)pendingBlocksBuffer_.data() = toBigEndian<int32_t>(dataBytes);
+ auto totalBytes = sizeof(ChunkHeader) + dataBytes;
+ if (!WriteFdExactly(adb_fd_, pendingBlocksBuffer_.data(), totalBytes)) {
+ fprintf(stderr, "Failed to write %d bytes\n", int(totalBytes));
}
- sentSize_ += pendingBlocks_.size();
- pendingBlocks_.clear();
+ sentSize_ += totalBytes;
+ pendingBlocks_ = pendingBlocksBuffer_.data() + sizeof(ChunkHeader);
}
-bool IncrementalServer::Exit(std::optional<TimePoint> startTime, int missesCount, int missesSent) {
+bool IncrementalServer::ServingComplete(std::optional<TimePoint> startTime, int missesCount,
+ int missesSent) {
+ servingComplete_ = true;
using namespace std::chrono;
auto endTime = high_resolution_clock::now();
- fprintf(stderr,
- "Connection failed or received exit command. Exit.\n"
- "Misses: %d, of those unique: %d; sent compressed: %d, uncompressed: "
- "%d, mb: %.3f\n"
- "Total time taken: %.3fms\n",
- missesCount, missesSent, compressed_, uncompressed_, sentSize_ / 1024.0 / 1024.0,
- duration_cast<microseconds>(endTime - (startTime ? *startTime : endTime)).count() /
- 1000.0);
+ D("Streaming completed.\n"
+ "Misses: %d, of those unique: %d; sent compressed: %d, uncompressed: "
+ "%d, mb: %.3f\n"
+ "Total time taken: %.3fms",
+ missesCount, missesSent, compressed_, uncompressed_, sentSize_ / 1024.0 / 1024.0,
+ duration_cast<microseconds>(endTime - (startTime ? *startTime : endTime)).count() / 1000.0);
return true;
}
@@ -425,7 +561,7 @@
std::all_of(files_.begin(), files_.end(), [](const File& f) {
return f.sentBlocksCount == NumBlocks(f.sentBlocks.size());
})) {
- fprintf(stdout, "All files should be loaded. Notifying the device.\n");
+ fprintf(stderr, "All files should be loaded. Notifying the device.\n");
SendDone();
doneSent = true;
}
@@ -446,9 +582,14 @@
BlockIdx blockIdx = request->block_idx;
switch (request->request_type) {
- case EXIT: {
+ case DESTROY: {
// Stop everything.
- return Exit(startTime, missesCount, missesSent);
+ return true;
+ }
+ case SERVING_COMPLETE: {
+ // Not stopping the server here.
+ ServingComplete(startTime, missesCount, missesSent);
+ break;
}
case BLOCK_MISSING: {
++missesCount;
@@ -461,9 +602,21 @@
fileId, blockIdx);
break;
}
- // fprintf(stderr, "\treading file %d block %04d\n", (int)fileId,
- // (int)blockIdx);
- if (auto res = SendBlock(fileId, blockIdx, true); res == SendResult::Error) {
+
+ if (VLOG_IS_ON(INCREMENTAL)) {
+ auto& file = files_[fileId];
+ auto posP = std::find(file.PriorityBlocks().begin(),
+ file.PriorityBlocks().end(), blockIdx);
+ D("\tMISSING BLOCK: reading file %d block %04d (in priority: %d of %d)",
+ (int)fileId, (int)blockIdx,
+ posP == file.PriorityBlocks().end()
+ ? -1
+ : int(posP - file.PriorityBlocks().begin()),
+ int(file.PriorityBlocks().size()));
+ }
+
+ if (auto res = SendDataBlock(fileId, blockIdx, true);
+ res == SendResult::Error) {
fprintf(stderr, "Failed to send block %" PRId32 ".\n", blockIdx);
} else if (res == SendResult::Sent) {
++missesSent;
@@ -487,7 +640,7 @@
fileId);
break;
}
- D("Received prefetch request for file_id %" PRId16 ".\n", fileId);
+ D("Received prefetch request for file_id %" PRId16 ".", fileId);
prefetches_.emplace_back(files_[fileId]);
break;
}
@@ -502,8 +655,47 @@
}
}
-bool serve(int adb_fd, int argc, const char** argv) {
- auto connection_fd = unique_fd(adb_fd);
+static std::pair<unique_fd, int64_t> open_fd(const char* filepath) {
+ struct stat st;
+ if (stat(filepath, &st)) {
+ error_exit("inc-server: failed to stat input file '%s'.", filepath);
+ }
+
+ unique_fd fd(adb_open(filepath, O_RDONLY));
+ if (fd < 0) {
+ error_exit("inc-server: failed to open file '%s'.", filepath);
+ }
+
+ return {std::move(fd), st.st_size};
+}
+
+static std::pair<unique_fd, int64_t> open_signature(int64_t file_size, const char* filepath) {
+ std::string signature_file(filepath);
+ signature_file += IDSIG;
+
+ unique_fd fd(adb_open(signature_file.c_str(), O_RDONLY));
+ if (fd < 0) {
+ D("No signature file found for '%s'('%s')", filepath, signature_file.c_str());
+ return {};
+ }
+
+ auto [tree_offset, tree_size] = skip_id_sig_headers(fd);
+ if (auto expected = verity_tree_size_for_file(file_size); tree_size != expected) {
+ error_exit("Verity tree size mismatch in signature file: %s [was %lld, expected %lld].\n",
+ signature_file.c_str(), (long long)tree_size, (long long)expected);
+ }
+
+ int32_t data_block_count = numBytesToNumBlocks(file_size);
+ int32_t leaf_nodes_count = (data_block_count + kHashesPerBlock - 1) / kHashesPerBlock;
+ D("Verity tree loaded: %s, tree size: %d (%d blocks, %d leafs)", signature_file.c_str(),
+ int(tree_size), int(numBytesToNumBlocks(tree_size)), int(leaf_nodes_count));
+
+ return {std::move(fd), tree_offset};
+}
+
+bool serve(int connection_fd, int output_fd, int argc, const char** argv) {
+ auto connection_ufd = unique_fd(connection_fd);
+ auto output_ufd = unique_fd(output_fd);
if (argc <= 0) {
error_exit("inc-server: must specify at least one file.");
}
@@ -513,20 +705,14 @@
for (int i = 0; i < argc; ++i) {
auto filepath = argv[i];
- struct stat st;
- if (stat(filepath, &st)) {
- fprintf(stderr, "Failed to stat input file %s. Abort.\n", filepath);
- return {};
- }
+ auto [file_fd, file_size] = open_fd(filepath);
+ auto [sign_fd, sign_offset] = open_signature(file_size, filepath);
- unique_fd fd(adb_open(filepath, O_RDONLY));
- if (fd < 0) {
- error_exit("inc-server: failed to open file '%s'.", filepath);
- }
- files.emplace_back(filepath, i, st.st_size, std::move(fd));
+ files.emplace_back(filepath, i, file_size, std::move(file_fd), sign_offset,
+ std::move(sign_fd));
}
- IncrementalServer server(std::move(connection_fd), std::move(files));
+ IncrementalServer server(std::move(connection_ufd), std::move(output_ufd), std::move(files));
printf("Serving...\n");
fclose(stdin);
fclose(stdout);
diff --git a/adb/client/incremental_server.h b/adb/client/incremental_server.h
index 53f011e..55b8215 100644
--- a/adb/client/incremental_server.h
+++ b/adb/client/incremental_server.h
@@ -21,6 +21,6 @@
// Expecting arguments like:
// {FILE1 FILE2 ...}
// Where FILE* are files to serve.
-bool serve(int adbFd, int argc, const char** argv);
+bool serve(int connection_fd, int output_fd, int argc, const char** argv);
} // namespace incremental
diff --git a/adb/client/incremental_utils.cpp b/adb/client/incremental_utils.cpp
new file mode 100644
index 0000000..dd117d2
--- /dev/null
+++ b/adb/client/incremental_utils.cpp
@@ -0,0 +1,378 @@
+/*
+ * 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.
+ */
+
+#define TRACE_TAG INCREMENTAL
+
+#include "incremental_utils.h"
+
+#include <android-base/endian.h>
+#include <android-base/mapped_file.h>
+#include <android-base/strings.h>
+#include <ziparchive/zip_archive.h>
+#include <ziparchive/zip_writer.h>
+
+#include <array>
+#include <cinttypes>
+#include <numeric>
+#include <unordered_set>
+
+#include "adb_io.h"
+#include "adb_trace.h"
+#include "sysdeps.h"
+
+using namespace std::literals;
+
+namespace incremental {
+
+static constexpr inline int32_t offsetToBlockIndex(int64_t offset) {
+ return (offset & ~(kBlockSize - 1)) >> 12;
+}
+
+Size verity_tree_blocks_for_file(Size fileSize) {
+ if (fileSize == 0) {
+ return 0;
+ }
+
+ constexpr int hash_per_block = kBlockSize / kDigestSize;
+
+ Size total_tree_block_count = 0;
+
+ auto block_count = 1 + (fileSize - 1) / kBlockSize;
+ auto hash_block_count = block_count;
+ for (auto i = 0; hash_block_count > 1; i++) {
+ hash_block_count = (hash_block_count + hash_per_block - 1) / hash_per_block;
+ total_tree_block_count += hash_block_count;
+ }
+ return total_tree_block_count;
+}
+
+Size verity_tree_size_for_file(Size fileSize) {
+ return verity_tree_blocks_for_file(fileSize) * kBlockSize;
+}
+
+static inline int32_t read_int32(borrowed_fd fd) {
+ int32_t result;
+ return ReadFdExactly(fd, &result, sizeof(result)) ? result : -1;
+}
+
+static inline int32_t skip_int(borrowed_fd fd) {
+ return adb_lseek(fd, 4, SEEK_CUR);
+}
+
+static inline void append_int(borrowed_fd fd, std::vector<char>* bytes) {
+ int32_t le_val = read_int32(fd);
+ auto old_size = bytes->size();
+ bytes->resize(old_size + sizeof(le_val));
+ memcpy(bytes->data() + old_size, &le_val, sizeof(le_val));
+}
+
+static inline void append_bytes_with_size(borrowed_fd fd, std::vector<char>* bytes) {
+ int32_t le_size = read_int32(fd);
+ if (le_size < 0) {
+ return;
+ }
+ int32_t size = int32_t(le32toh(le_size));
+ auto old_size = bytes->size();
+ bytes->resize(old_size + sizeof(le_size) + size);
+ memcpy(bytes->data() + old_size, &le_size, sizeof(le_size));
+ ReadFdExactly(fd, bytes->data() + old_size + sizeof(le_size), size);
+}
+
+static inline int32_t skip_bytes_with_size(borrowed_fd fd) {
+ int32_t le_size = read_int32(fd);
+ if (le_size < 0) {
+ return -1;
+ }
+ int32_t size = int32_t(le32toh(le_size));
+ return (int32_t)adb_lseek(fd, size, SEEK_CUR);
+}
+
+std::pair<std::vector<char>, int32_t> read_id_sig_headers(borrowed_fd fd) {
+ std::vector<char> result;
+ append_int(fd, &result); // version
+ append_bytes_with_size(fd, &result); // hashingInfo
+ append_bytes_with_size(fd, &result); // signingInfo
+ auto le_tree_size = read_int32(fd);
+ auto tree_size = int32_t(le32toh(le_tree_size)); // size of the verity tree
+ return {std::move(result), tree_size};
+}
+
+std::pair<off64_t, ssize_t> skip_id_sig_headers(borrowed_fd fd) {
+ skip_int(fd); // version
+ skip_bytes_with_size(fd); // hashingInfo
+ auto offset = skip_bytes_with_size(fd); // signingInfo
+ auto le_tree_size = read_int32(fd);
+ auto tree_size = int32_t(le32toh(le_tree_size)); // size of the verity tree
+ return {offset + sizeof(le_tree_size), tree_size};
+}
+
+template <class T>
+static T valueAt(borrowed_fd fd, off64_t offset) {
+ T t;
+ memset(&t, 0, sizeof(T));
+ if (adb_pread(fd, &t, sizeof(T), offset) != sizeof(T)) {
+ memset(&t, -1, sizeof(T));
+ }
+
+ return t;
+}
+
+static void appendBlocks(int32_t start, int count, std::vector<int32_t>* blocks) {
+ if (count == 1) {
+ blocks->push_back(start);
+ } else {
+ auto oldSize = blocks->size();
+ blocks->resize(oldSize + count);
+ std::iota(blocks->begin() + oldSize, blocks->end(), start);
+ }
+}
+
+template <class T>
+static void unduplicate(std::vector<T>& v) {
+ std::unordered_set<T> uniques(v.size());
+ v.erase(std::remove_if(v.begin(), v.end(),
+ [&uniques](T t) { return !uniques.insert(t).second; }),
+ v.end());
+}
+
+static off64_t CentralDirOffset(borrowed_fd fd, Size fileSize) {
+ static constexpr int kZipEocdRecMinSize = 22;
+ static constexpr int32_t kZipEocdRecSig = 0x06054b50;
+ static constexpr int kZipEocdCentralDirSizeFieldOffset = 12;
+ static constexpr int kZipEocdCommentLengthFieldOffset = 20;
+
+ int32_t sigBuf = 0;
+ off64_t eocdOffset = -1;
+ off64_t maxEocdOffset = fileSize - kZipEocdRecMinSize;
+ int16_t commentLenBuf = 0;
+
+ // Search from the end of zip, backward to find beginning of EOCD
+ for (int16_t commentLen = 0; commentLen < fileSize; ++commentLen) {
+ sigBuf = valueAt<int32_t>(fd, maxEocdOffset - commentLen);
+ if (sigBuf == kZipEocdRecSig) {
+ commentLenBuf = valueAt<int16_t>(
+ fd, maxEocdOffset - commentLen + kZipEocdCommentLengthFieldOffset);
+ if (commentLenBuf == commentLen) {
+ eocdOffset = maxEocdOffset - commentLen;
+ break;
+ }
+ }
+ }
+
+ if (eocdOffset < 0) {
+ return -1;
+ }
+
+ off64_t cdLen = static_cast<int64_t>(
+ valueAt<int32_t>(fd, eocdOffset + kZipEocdCentralDirSizeFieldOffset));
+
+ return eocdOffset - cdLen;
+}
+
+// Does not support APKs larger than 4GB
+static off64_t SignerBlockOffset(borrowed_fd fd, Size fileSize) {
+ static constexpr int kApkSigBlockMinSize = 32;
+ static constexpr int kApkSigBlockFooterSize = 24;
+ static constexpr int64_t APK_SIG_BLOCK_MAGIC_HI = 0x3234206b636f6c42l;
+ static constexpr int64_t APK_SIG_BLOCK_MAGIC_LO = 0x20676953204b5041l;
+
+ off64_t cdOffset = CentralDirOffset(fd, fileSize);
+ if (cdOffset < 0) {
+ return -1;
+ }
+ // CD offset is where original signer block ends. Search backwards for magic and footer.
+ if (cdOffset < kApkSigBlockMinSize ||
+ valueAt<int64_t>(fd, cdOffset - 2 * sizeof(int64_t)) != APK_SIG_BLOCK_MAGIC_LO ||
+ valueAt<int64_t>(fd, cdOffset - sizeof(int64_t)) != APK_SIG_BLOCK_MAGIC_HI) {
+ return -1;
+ }
+ int32_t signerSizeInFooter = valueAt<int32_t>(fd, cdOffset - kApkSigBlockFooterSize);
+ off64_t signerBlockOffset = cdOffset - signerSizeInFooter - sizeof(int64_t);
+ if (signerBlockOffset < 0) {
+ return -1;
+ }
+ int32_t signerSizeInHeader = valueAt<int32_t>(fd, signerBlockOffset);
+ if (signerSizeInFooter != signerSizeInHeader) {
+ return -1;
+ }
+
+ return signerBlockOffset;
+}
+
+static std::vector<int32_t> ZipPriorityBlocks(off64_t signerBlockOffset, Size fileSize) {
+ int32_t signerBlockIndex = offsetToBlockIndex(signerBlockOffset);
+ int32_t lastBlockIndex = offsetToBlockIndex(fileSize);
+ const auto numPriorityBlocks = lastBlockIndex - signerBlockIndex + 1;
+
+ std::vector<int32_t> zipPriorityBlocks;
+
+ // Some magic here: most of zip libraries perform a scan for EOCD record starting at the offset
+ // of a maximum comment size from the end of the file. This means the last 65-ish KBs will be
+ // accessed first, followed by the rest of the central directory blocks. Make sure we
+ // send the data in the proper order, as central directory can be quite big by itself.
+ static constexpr auto kMaxZipCommentSize = 64 * 1024;
+ static constexpr auto kNumBlocksInEocdSearch = kMaxZipCommentSize / kBlockSize + 1;
+ if (numPriorityBlocks > kNumBlocksInEocdSearch) {
+ appendBlocks(lastBlockIndex - kNumBlocksInEocdSearch + 1, kNumBlocksInEocdSearch,
+ &zipPriorityBlocks);
+ appendBlocks(signerBlockIndex, numPriorityBlocks - kNumBlocksInEocdSearch,
+ &zipPriorityBlocks);
+ } else {
+ appendBlocks(signerBlockIndex, numPriorityBlocks, &zipPriorityBlocks);
+ }
+
+ // Somehow someone keeps accessing the start of the archive, even if there's nothing really
+ // interesting there...
+ appendBlocks(0, 1, &zipPriorityBlocks);
+ return zipPriorityBlocks;
+}
+
+[[maybe_unused]] static ZipArchiveHandle openZipArchiveFd(borrowed_fd fd) {
+ bool transferFdOwnership = false;
+#ifdef _WIN32
+ //
+ // Need to create a special CRT FD here as the current one is not compatible with
+ // normal read()/write() calls that libziparchive uses.
+ // To make this work we have to create a copy of the file handle, as CRT doesn't care
+ // and closes it together with the new descriptor.
+ //
+ // Note: don't move this into a helper function, it's better to be hard to reuse because
+ // the code is ugly and won't work unless it's a last resort.
+ //
+ auto handle = adb_get_os_handle(fd);
+ HANDLE dupedHandle;
+ if (!::DuplicateHandle(::GetCurrentProcess(), handle, ::GetCurrentProcess(), &dupedHandle, 0,
+ false, DUPLICATE_SAME_ACCESS)) {
+ D("%s failed at DuplicateHandle: %d", __func__, (int)::GetLastError());
+ return {};
+ }
+ int osfd = _open_osfhandle((intptr_t)dupedHandle, _O_RDONLY | _O_BINARY);
+ if (osfd < 0) {
+ D("%s failed at _open_osfhandle: %d", __func__, errno);
+ ::CloseHandle(handle);
+ return {};
+ }
+ transferFdOwnership = true;
+#else
+ int osfd = fd.get();
+#endif
+ ZipArchiveHandle zip;
+ if (OpenArchiveFd(osfd, "apk_fd", &zip, transferFdOwnership) != 0) {
+ D("%s failed at OpenArchiveFd: %d", __func__, errno);
+#ifdef _WIN32
+ // "_close()" is a secret WinCRT name for the regular close() function.
+ _close(osfd);
+#endif
+ return {};
+ }
+ return zip;
+}
+
+static std::pair<ZipArchiveHandle, std::unique_ptr<android::base::MappedFile>> openZipArchive(
+ borrowed_fd fd, Size fileSize) {
+#ifndef __LP64__
+ if (fileSize >= INT_MAX) {
+ return {openZipArchiveFd(fd), nullptr};
+ }
+#endif
+ auto mapping =
+ android::base::MappedFile::FromOsHandle(adb_get_os_handle(fd), 0, fileSize, PROT_READ);
+ if (!mapping) {
+ D("%s failed at FromOsHandle: %d", __func__, errno);
+ return {};
+ }
+ ZipArchiveHandle zip;
+ if (OpenArchiveFromMemory(mapping->data(), mapping->size(), "apk_mapping", &zip) != 0) {
+ D("%s failed at OpenArchiveFromMemory: %d", __func__, errno);
+ return {};
+ }
+ return {zip, std::move(mapping)};
+}
+
+static std::vector<int32_t> InstallationPriorityBlocks(borrowed_fd fd, Size fileSize) {
+ static constexpr std::array<std::string_view, 3> additional_matches = {
+ "resources.arsc"sv, "AndroidManifest.xml"sv, "classes.dex"sv};
+ auto [zip, _] = openZipArchive(fd, fileSize);
+ if (!zip) {
+ return {};
+ }
+
+ auto matcher = [](std::string_view entry_name) {
+ if (entry_name.starts_with("lib/"sv) && entry_name.ends_with(".so"sv)) {
+ return true;
+ }
+ return std::any_of(additional_matches.begin(), additional_matches.end(),
+ [entry_name](std::string_view i) { return i == entry_name; });
+ };
+
+ void* cookie = nullptr;
+ if (StartIteration(zip, &cookie, std::move(matcher)) != 0) {
+ D("%s failed at StartIteration: %d", __func__, errno);
+ return {};
+ }
+
+ std::vector<int32_t> installationPriorityBlocks;
+ ZipEntry entry;
+ std::string_view entryName;
+ while (Next(cookie, &entry, &entryName) == 0) {
+ if (entryName == "classes.dex"sv) {
+ // Only the head is needed for installation
+ int32_t startBlockIndex = offsetToBlockIndex(entry.offset);
+ appendBlocks(startBlockIndex, 2, &installationPriorityBlocks);
+ D("\tadding to priority blocks: '%.*s' (%d)", (int)entryName.size(), entryName.data(),
+ 2);
+ } else {
+ // Full entries are needed for installation
+ off64_t entryStartOffset = entry.offset;
+ off64_t entryEndOffset =
+ entryStartOffset +
+ (entry.method == kCompressStored ? entry.uncompressed_length
+ : entry.compressed_length) +
+ (entry.has_data_descriptor ? 16 /* sizeof(DataDescriptor) */ : 0);
+ int32_t startBlockIndex = offsetToBlockIndex(entryStartOffset);
+ int32_t endBlockIndex = offsetToBlockIndex(entryEndOffset);
+ int32_t numNewBlocks = endBlockIndex - startBlockIndex + 1;
+ appendBlocks(startBlockIndex, numNewBlocks, &installationPriorityBlocks);
+ D("\tadding to priority blocks: '%.*s' (%d)", (int)entryName.size(), entryName.data(),
+ numNewBlocks);
+ }
+ }
+
+ EndIteration(cookie);
+ CloseArchive(zip);
+ return installationPriorityBlocks;
+}
+
+std::vector<int32_t> PriorityBlocksForFile(const std::string& filepath, borrowed_fd fd,
+ Size fileSize) {
+ if (!android::base::EndsWithIgnoreCase(filepath, ".apk"sv)) {
+ return {};
+ }
+ off64_t signerOffset = SignerBlockOffset(fd, fileSize);
+ if (signerOffset < 0) {
+ // No signer block? not a valid APK
+ return {};
+ }
+ std::vector<int32_t> priorityBlocks = ZipPriorityBlocks(signerOffset, fileSize);
+ std::vector<int32_t> installationPriorityBlocks = InstallationPriorityBlocks(fd, fileSize);
+
+ priorityBlocks.insert(priorityBlocks.end(), installationPriorityBlocks.begin(),
+ installationPriorityBlocks.end());
+ unduplicate(priorityBlocks);
+ return priorityBlocks;
+}
+
+} // namespace incremental
diff --git a/adb/client/incremental_utils.h b/adb/client/incremental_utils.h
new file mode 100644
index 0000000..4ad60dd
--- /dev/null
+++ b/adb/client/incremental_utils.h
@@ -0,0 +1,49 @@
+/*
+ * 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 "adb_unique_fd.h"
+
+#include <string>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+#include <stdint.h>
+
+#include <android-base/off64_t.h>
+
+namespace incremental {
+
+using Size = int64_t;
+constexpr int kBlockSize = 4096;
+constexpr int kSha256DigestSize = 32;
+constexpr int kDigestSize = kSha256DigestSize;
+constexpr int kMaxSignatureSize = 8096; // incrementalfs.h
+
+constexpr std::string_view IDSIG = ".idsig";
+
+std::vector<int32_t> PriorityBlocksForFile(const std::string& filepath, borrowed_fd fd,
+ Size fileSize);
+
+Size verity_tree_blocks_for_file(Size fileSize);
+Size verity_tree_size_for_file(Size fileSize);
+
+std::pair<std::vector<char>, int32_t> read_id_sig_headers(borrowed_fd fd);
+std::pair<off64_t, ssize_t> skip_id_sig_headers(borrowed_fd fd);
+
+} // namespace incremental
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index a85a18c..a19bd6d 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -36,6 +36,7 @@
#include "adb_listeners.h"
#include "adb_utils.h"
#include "adb_wifi.h"
+#include "client/usb.h"
#include "commandline.h"
#include "sysdeps/chrono.h"
#include "transport.h"
@@ -104,7 +105,12 @@
fdevent_run_on_main_thread([]() { exit(0); });
});
- char* leak = getenv("ADB_LEAK");
+ const char* reject_kill_server = getenv("ADB_REJECT_KILL_SERVER");
+ if (reject_kill_server && strcmp(reject_kill_server, "1") == 0) {
+ adb_set_reject_kill_server(true);
+ }
+
+ const char* leak = getenv("ADB_LEAK");
if (leak && strcmp(leak, "1") == 0) {
intentionally_leak();
}
@@ -139,9 +145,10 @@
auto start = std::chrono::steady_clock::now();
// If we told a previous adb server to quit because of version mismatch, we can get to this
- // point before it's finished exiting. Retry for a while to give it some time.
- while (install_listener(socket_spec, "*smartsocket*", nullptr, 0, nullptr, &error) !=
- INSTALL_STATUS_OK) {
+ // point before it's finished exiting. Retry for a while to give it some time. Don't actually
+ // accept any connections until adb_wait_for_device_initialization finishes below.
+ while (install_listener(socket_spec, "*smartsocket*", nullptr, INSTALL_LISTENER_DISABLED,
+ nullptr, &error) != INSTALL_STATUS_OK) {
if (std::chrono::steady_clock::now() - start > 0.5s) {
LOG(FATAL) << "could not install *smartsocket* listener: " << error;
}
@@ -162,12 +169,14 @@
PLOG(FATAL) << "setsid() failed";
}
#endif
+ }
- // Wait for the USB scan to complete before notifying the parent that we're up.
- // We need to perform this in a thread, because we would otherwise block the event loop.
- std::thread notify_thread([ack_reply_fd]() {
- adb_wait_for_device_initialization();
+ // Wait for the USB scan to complete before notifying the parent that we're up.
+ // We need to perform this in a thread, because we would otherwise block the event loop.
+ std::thread notify_thread([ack_reply_fd]() {
+ adb_wait_for_device_initialization();
+ if (ack_reply_fd >= 0) {
// Any error output written to stderr now goes to adb.log. We could
// keep around a copy of the stderr fd and use that to write any errors
// encountered by the following code, but that is probably overkill.
@@ -193,9 +202,13 @@
}
unix_close(ack_reply_fd);
#endif
- });
- notify_thread.detach();
- }
+ }
+ // We don't accept() client connections until this point: this way, clients
+ // can't see wonky state early in startup even if they're connecting directly
+ // to the server instead of going through the adb program.
+ fdevent_run_on_main_thread([] { enable_server_sockets(); });
+ });
+ notify_thread.detach();
#if defined(__linux__)
// Write our location to .android/adb.$PORT, so that older clients can exec us.
diff --git a/adb/client/mdns_utils.cpp b/adb/client/mdns_utils.cpp
new file mode 100644
index 0000000..8666b18
--- /dev/null
+++ b/adb/client/mdns_utils.cpp
@@ -0,0 +1,77 @@
+/*
+ * 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 "client/mdns_utils.h"
+
+#include <android-base/strings.h>
+
+namespace mdns {
+
+// <Instance>.<Service>.<Domain>
+std::optional<MdnsInstance> mdns_parse_instance_name(std::string_view name) {
+ CHECK(!name.empty());
+
+ // Return the whole name if it doesn't fall under <Instance>.<Service>.<Domain> or
+ // <Instance>.<Service>
+ bool has_local_suffix = false;
+ // Strip the local suffix, if any
+ {
+ std::string local_suffix = ".local";
+ local_suffix += android::base::EndsWith(name, ".") ? "." : "";
+
+ if (android::base::ConsumeSuffix(&name, local_suffix)) {
+ if (name.empty()) {
+ return std::nullopt;
+ }
+ has_local_suffix = true;
+ }
+ }
+
+ std::string transport;
+ // Strip the transport suffix, if any
+ {
+ std::string add_dot = (!has_local_suffix && android::base::EndsWith(name, ".")) ? "." : "";
+ std::array<std::string, 2> transport_suffixes{"._tcp", "._udp"};
+
+ for (const auto& t : transport_suffixes) {
+ if (android::base::ConsumeSuffix(&name, t + add_dot)) {
+ if (name.empty()) {
+ return std::nullopt;
+ }
+ transport = t.substr(1);
+ break;
+ }
+ }
+
+ if (has_local_suffix && transport.empty()) {
+ return std::nullopt;
+ }
+ }
+
+ if (!has_local_suffix && transport.empty()) {
+ return std::make_optional<MdnsInstance>(name, "", "");
+ }
+
+ // Split the service name from the instance name
+ auto pos = name.rfind(".");
+ if (pos == 0 || pos == std::string::npos || pos == name.size() - 1) {
+ return std::nullopt;
+ }
+
+ return std::make_optional<MdnsInstance>(name.substr(0, pos), name.substr(pos + 1), transport);
+}
+
+} // namespace mdns
diff --git a/adb/client/mdns_utils.h b/adb/client/mdns_utils.h
new file mode 100644
index 0000000..40d095d
--- /dev/null
+++ b/adb/client/mdns_utils.h
@@ -0,0 +1,54 @@
+/*
+ * 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 <optional>
+#include <string_view>
+
+#include "adb_wifi.h"
+
+namespace mdns {
+
+struct MdnsInstance {
+ std::string instance_name; // "my name"
+ std::string service_name; // "_adb-tls-connect"
+ std::string transport_type; // either "_tcp" or "_udp"
+
+ MdnsInstance(std::string_view inst, std::string_view serv, std::string_view trans)
+ : instance_name(inst), service_name(serv), transport_type(trans) {}
+};
+
+// This parser is based on https://tools.ietf.org/html/rfc6763#section-4.1 for
+// structured service instance names, where the whole name is in the format
+// <Instance>.<Service>.<Domain>.
+//
+// In our case, we ignore <Domain> portion of the name, which
+// we always assume to be ".local", or link-local mDNS.
+//
+// The string can be in one of the following forms:
+// - <Instance>.<Service>.<Domain>.?
+// - e.g. "instance._service._tcp.local" (or "...local.")
+// - <Instance>.<Service>.? (must contain either "_tcp" or "_udp" at the end)
+// - e.g. "instance._service._tcp" (or "..._tcp.)
+// - <Instance> (can contain dots '.')
+// - e.g. "myname", "name.", "my.name."
+//
+// Returns an MdnsInstance with the appropriate fields filled in (instance name is never empty),
+// otherwise returns std::nullopt.
+std::optional<MdnsInstance> mdns_parse_instance_name(std::string_view name);
+
+} // namespace mdns
diff --git a/adb/client/mdns_utils_test.cpp b/adb/client/mdns_utils_test.cpp
new file mode 100644
index 0000000..ec71529
--- /dev/null
+++ b/adb/client/mdns_utils_test.cpp
@@ -0,0 +1,173 @@
+/*
+ * 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 "client/mdns_utils.h"
+
+#include <gtest/gtest.h>
+
+namespace mdns {
+
+TEST(mdns_utils, mdns_parse_instance_name) {
+ // Just the instance name
+ {
+ std::string str = ".";
+ auto res = mdns_parse_instance_name(str);
+ ASSERT_TRUE(res.has_value());
+ EXPECT_EQ(str, res->instance_name);
+ EXPECT_TRUE(res->service_name.empty());
+ EXPECT_TRUE(res->transport_type.empty());
+ }
+ {
+ std::string str = "my.name";
+ auto res = mdns_parse_instance_name(str);
+ ASSERT_TRUE(res.has_value());
+ EXPECT_EQ(str, res->instance_name);
+ EXPECT_TRUE(res->service_name.empty());
+ EXPECT_TRUE(res->transport_type.empty());
+ }
+ {
+ std::string str = "my.name.";
+ auto res = mdns_parse_instance_name(str);
+ ASSERT_TRUE(res.has_value());
+ EXPECT_EQ(str, res->instance_name);
+ EXPECT_TRUE(res->service_name.empty());
+ EXPECT_TRUE(res->transport_type.empty());
+ }
+
+ // With "_tcp", "_udp" transport type
+ for (const std::string_view transport : {"._tcp", "._udp"}) {
+ {
+ std::string str = android::base::StringPrintf("%s", transport.data());
+ auto res = mdns_parse_instance_name(str);
+ EXPECT_FALSE(res.has_value());
+ }
+ {
+ std::string str = android::base::StringPrintf("%s.", transport.data());
+ auto res = mdns_parse_instance_name(str);
+ EXPECT_FALSE(res.has_value());
+ }
+ {
+ std::string str = android::base::StringPrintf("service%s", transport.data());
+ auto res = mdns_parse_instance_name(str);
+ EXPECT_FALSE(res.has_value());
+ }
+ {
+ std::string str = android::base::StringPrintf(".service%s", transport.data());
+ auto res = mdns_parse_instance_name(str);
+ EXPECT_FALSE(res.has_value());
+ }
+ {
+ std::string str = android::base::StringPrintf("service.%s", transport.data());
+ auto res = mdns_parse_instance_name(str);
+ EXPECT_FALSE(res.has_value());
+ }
+ {
+ std::string str = android::base::StringPrintf("my.service%s", transport.data());
+ auto res = mdns_parse_instance_name(str);
+ ASSERT_TRUE(res.has_value());
+ EXPECT_EQ(res->instance_name, "my");
+ EXPECT_EQ(res->service_name, "service");
+ EXPECT_EQ(res->transport_type, transport.substr(1));
+ }
+ {
+ std::string str = android::base::StringPrintf("my.service%s.", transport.data());
+ auto res = mdns_parse_instance_name(str);
+ ASSERT_TRUE(res.has_value());
+ EXPECT_EQ(res->instance_name, "my");
+ EXPECT_EQ(res->service_name, "service");
+ EXPECT_EQ(res->transport_type, transport.substr(1));
+ }
+ {
+ std::string str = android::base::StringPrintf("my..service%s", transport.data());
+ auto res = mdns_parse_instance_name(str);
+ ASSERT_TRUE(res.has_value());
+ EXPECT_EQ(res->instance_name, "my.");
+ EXPECT_EQ(res->service_name, "service");
+ EXPECT_EQ(res->transport_type, transport.substr(1));
+ }
+ {
+ std::string str = android::base::StringPrintf("my.name.service%s.", transport.data());
+ auto res = mdns_parse_instance_name(str);
+ ASSERT_TRUE(res.has_value());
+ EXPECT_EQ(res->instance_name, "my.name");
+ EXPECT_EQ(res->service_name, "service");
+ EXPECT_EQ(res->transport_type, transport.substr(1));
+ }
+ {
+ std::string str = android::base::StringPrintf("name.service.%s.", transport.data());
+ auto res = mdns_parse_instance_name(str);
+ EXPECT_FALSE(res.has_value());
+ }
+
+ // With ".local" domain
+ {
+ std::string str = ".local";
+ auto res = mdns_parse_instance_name(str);
+ EXPECT_FALSE(res.has_value());
+ }
+ {
+ std::string str = ".local.";
+ auto res = mdns_parse_instance_name(str);
+ EXPECT_FALSE(res.has_value());
+ }
+ {
+ std::string str = "name.local";
+ auto res = mdns_parse_instance_name(str);
+ EXPECT_FALSE(res.has_value());
+ }
+ {
+ std::string str = android::base::StringPrintf("%s.local", transport.data());
+ auto res = mdns_parse_instance_name(str);
+ EXPECT_FALSE(res.has_value());
+ }
+ {
+ std::string str = android::base::StringPrintf("service%s.local", transport.data());
+ auto res = mdns_parse_instance_name(str);
+ EXPECT_FALSE(res.has_value());
+ }
+ {
+ std::string str = android::base::StringPrintf("name.service%s.local", transport.data());
+ auto res = mdns_parse_instance_name(str);
+ ASSERT_TRUE(res.has_value());
+ EXPECT_EQ(res->instance_name, "name");
+ EXPECT_EQ(res->service_name, "service");
+ EXPECT_EQ(res->transport_type, transport.substr(1));
+ }
+ {
+ std::string str =
+ android::base::StringPrintf("name.service%s.local.", transport.data());
+ auto res = mdns_parse_instance_name(str);
+ ASSERT_TRUE(res.has_value());
+ EXPECT_EQ(res->instance_name, "name");
+ EXPECT_EQ(res->service_name, "service");
+ EXPECT_EQ(res->transport_type, transport.substr(1));
+ }
+ {
+ std::string str =
+ android::base::StringPrintf("name.service%s..local.", transport.data());
+ auto res = mdns_parse_instance_name(str);
+ EXPECT_FALSE(res.has_value());
+ }
+ {
+ std::string str =
+ android::base::StringPrintf("name.service.%s.local.", transport.data());
+ auto res = mdns_parse_instance_name(str);
+ EXPECT_FALSE(res.has_value());
+ }
+ }
+}
+
+} // namespace mdns
diff --git a/adb/client/pairing/pairing_client.cpp b/adb/client/pairing/pairing_client.cpp
index 2f878bf..937a5bd 100644
--- a/adb/client/pairing/pairing_client.cpp
+++ b/adb/client/pairing/pairing_client.cpp
@@ -141,7 +141,8 @@
cert_.size(), priv_key_.data(), priv_key_.size()));
CHECK(connection_);
- if (!pairing_connection_start(connection_.get(), fd.release(), OnPairingResult, this)) {
+ int osh = cast_handle_to_int(adb_get_os_handle(fd.release()));
+ if (!pairing_connection_start(connection_.get(), osh, OnPairingResult, this)) {
LOG(ERROR) << "PairingClient failed to start the PairingConnection";
state_ = State::Stopped;
return false;
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 22b9b18..9db2453 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>
@@ -37,32 +38,81 @@
#include "adb_trace.h"
#include "adb_utils.h"
#include "adb_wifi.h"
+#include "client/mdns_utils.h"
#include "fdevent/fdevent.h"
#include "sysdeps.h"
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()
@@ -95,7 +145,7 @@
return initialized_;
}
- virtual ~AsyncServiceRef() {
+ void DestroyServiceRef() {
if (!initialized_) {
return;
}
@@ -103,9 +153,13 @@
// Order matters here! Must destroy the fdevent first since it has a
// reference to |sdRef_|.
fdevent_destroy(fde_);
+ D("DNSServiceRefDeallocate(sdRef=%p)", sdRef_);
DNSServiceRefDeallocate(sdRef_);
+ initialized_ = false;
}
+ virtual ~AsyncServiceRef() { DestroyServiceRef(); }
+
protected:
DNSServiceRef sdRef_;
@@ -154,6 +208,7 @@
if (ret != kDNSServiceErr_NoError) {
D("Got %d from DNSServiceGetAddrInfo.", ret);
} else {
+ D("DNSServiceGetAddrInfo(sdRef=%p, hosttarget=%s)", sdRef_, hosttarget);
Initialize();
}
@@ -167,14 +222,14 @@
}
std::string response;
- connect_device(android::base::StringPrintf(addr_format_.c_str(), ip_addr_, port_),
+ connect_device(android::base::StringPrintf("%s.%s", serviceName_.c_str(), regType_.c_str()),
&response);
D("Secure connect to %s regtype %s (%s:%hu) : %s", serviceName_.c_str(), regType_.c_str(),
ip_addr_, port_, response.c_str());
return true;
}
- void Connect(const sockaddr* address) {
+ bool AddToServiceRegistry(const sockaddr* address) {
sa_family_ = address->sa_family;
if (sa_family_ == AF_INET) {
@@ -185,26 +240,53 @@
addr_format_ = "[%s]:%hu";
} else { // Should be impossible
D("mDNS resolved non-IP address.");
- return;
+ return false;
}
// Winsock version requires the const cast Because Microsoft.
if (!inet_ntop(sa_family_, const_cast<void*>(ip_addr_data_), ip_addr_, sizeof(ip_addr_))) {
D("Could not convert IP address to string.");
- return;
+ return false;
}
- // adb secure service needs to do something different from just
- // connecting here.
- if (adb_DNSServiceShouldConnect(regType_.c_str(), serviceName_.c_str())) {
+ // Add to the service registry before trying to auto-connect, since socket_spec_connect will
+ // check these registries for the ip address when connecting via mdns instance name.
+ int adbSecureServiceType = serviceIndex();
+ ServiceRegistry* services = nullptr;
+ switch (adbSecureServiceType) {
+ case kADBTransportServiceRefIndex:
+ services = sAdbTransportServices;
+ break;
+ case kADBSecurePairingServiceRefIndex:
+ services = sAdbSecurePairingServices;
+ break;
+ case kADBSecureConnectServiceRefIndex:
+ services = sAdbSecureConnectServices;
+ break;
+ default:
+ LOG(WARNING) << "No registry available for reg_type=[" << regType_ << "]";
+ return false;
+ }
+
+ if (!services->empty()) {
+ // Remove the previous resolved service, if any.
+ services->erase(std::remove_if(services->begin(), services->end(),
+ [&](std::unique_ptr<ResolvedService>& service) {
+ return (serviceName_ == service->serviceName());
+ }));
+ }
+ services->push_back(std::unique_ptr<ResolvedService>(this));
+
+ 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_);
+ D("Attempting to connect serviceName=[%s], regtype=[%s] ipaddr=(%s:%hu)",
+ serviceName_.c_str(), regType_.c_str(), ip_addr_, port_);
int index = adb_DNSServiceIndexByName(regType_.c_str());
if (index == kADBSecureConnectServiceRefIndex) {
ConnectSecureWifiDevice();
} else {
- connect_device(android::base::StringPrintf(addr_format_.c_str(), ip_addr_, port_),
+ connect_device(android::base::StringPrintf("%s.%s", serviceName_.c_str(),
+ regType_.c_str()),
&response);
D("Connect to %s regtype %s (%s:%hu) : %s", serviceName_.c_str(), regType_.c_str(),
ip_addr_, port_, response.c_str());
@@ -214,17 +296,7 @@
serviceName_.c_str(), regType_.c_str(), ip_addr_, port_);
}
- int adbSecureServiceType = serviceIndex();
- switch (adbSecureServiceType) {
- case kADBSecurePairingServiceRefIndex:
- sAdbSecurePairingServices->push_back(this);
- break;
- case kADBSecureConnectServiceRefIndex:
- sAdbSecureConnectServices->push_back(this);
- break;
- default:
- break;
- }
+ return true;
}
int serviceIndex() const { return adb_DNSServiceIndexByName(regType_.c_str()); }
@@ -233,18 +305,23 @@
std::string serviceName() const { return serviceName_; }
+ std::string regType() const { return regType_; }
+
std::string ipAddress() const { return ip_addr_; }
uint16_t port() const { return port_; }
- using ServiceRegistry = std::vector<ResolvedService*>;
+ using ServiceRegistry = std::vector<std::unique_ptr<ResolvedService>>;
+
+ // unencrypted tcp connections
+ static ServiceRegistry* sAdbTransportServices;
static ServiceRegistry* sAdbSecurePairingServices;
static ServiceRegistry* sAdbSecureConnectServices;
- static void initAdbSecure();
+ static void initAdbServiceRegistries();
- static void forEachService(const ServiceRegistry& services, const std::string& hostname,
+ static void forEachService(const ServiceRegistry& services, std::string_view hostname,
adb_secure_foreach_service_callback cb);
static bool connectByServiceName(const ServiceRegistry& services,
@@ -264,13 +341,19 @@
};
// static
-std::vector<ResolvedService*>* ResolvedService::sAdbSecurePairingServices = NULL;
+ResolvedService::ServiceRegistry* ResolvedService::sAdbTransportServices = NULL;
// static
-std::vector<ResolvedService*>* ResolvedService::sAdbSecureConnectServices = NULL;
+ResolvedService::ServiceRegistry* ResolvedService::sAdbSecurePairingServices = NULL;
// static
-void ResolvedService::initAdbSecure() {
+ResolvedService::ServiceRegistry* ResolvedService::sAdbSecureConnectServices = NULL;
+
+// static
+void ResolvedService::initAdbServiceRegistries() {
+ if (!sAdbTransportServices) {
+ sAdbTransportServices = new ServiceRegistry;
+ }
if (!sAdbSecurePairingServices) {
sAdbSecurePairingServices = new ServiceRegistry;
}
@@ -281,19 +364,20 @@
// static
void ResolvedService::forEachService(const ServiceRegistry& services,
- const std::string& wanted_service_name,
+ std::string_view wanted_service_name,
adb_secure_foreach_service_callback cb) {
- initAdbSecure();
+ initAdbServiceRegistries();
- for (auto service : services) {
+ for (const auto& service : services) {
auto service_name = service->serviceName();
+ auto reg_type = service->regType();
auto ip = service->ipAddress();
auto port = service->port();
- if (wanted_service_name == "") {
- cb(service_name.c_str(), ip.c_str(), port);
+ if (wanted_service_name.empty()) {
+ cb(service_name.c_str(), reg_type.c_str(), ip.c_str(), port);
} else if (service_name == wanted_service_name) {
- cb(service_name.c_str(), ip.c_str(), port);
+ cb(service_name.c_str(), reg_type.c_str(), ip.c_str(), port);
}
}
}
@@ -301,8 +385,8 @@
// static
bool ResolvedService::connectByServiceName(const ServiceRegistry& services,
const std::string& service_name) {
- initAdbSecure();
- for (auto service : services) {
+ initAdbServiceRegistries();
+ for (const auto& service : services) {
if (service_name == service->serviceName()) {
D("Got service_name match [%s]", service->serviceName().c_str());
return service->ConnectSecureWifiDevice();
@@ -314,14 +398,12 @@
void adb_secure_foreach_pairing_service(const char* service_name,
adb_secure_foreach_service_callback cb) {
- ResolvedService::forEachService(*ResolvedService::sAdbSecurePairingServices,
- service_name ? service_name : "", cb);
+ ResolvedService::forEachService(*ResolvedService::sAdbSecurePairingServices, service_name, cb);
}
void adb_secure_foreach_connect_service(const char* service_name,
adb_secure_foreach_service_callback cb) {
- ResolvedService::forEachService(*ResolvedService::sAdbSecureConnectServices,
- service_name ? service_name : "", cb);
+ ResolvedService::forEachService(*ResolvedService::sAdbSecureConnectServices, service_name, cb);
}
bool adb_secure_connect_by_service_name(const char* service_name) {
@@ -329,23 +411,28 @@
service_name);
}
-static void DNSSD_API register_service_ip(DNSServiceRef /*sdRef*/,
- DNSServiceFlags /*flags*/,
+static void DNSSD_API register_service_ip(DNSServiceRef sdRef, DNSServiceFlags flags,
uint32_t /*interfaceIndex*/,
- DNSServiceErrorType /*errorCode*/,
- const char* /*hostname*/,
- const sockaddr* address,
- uint32_t /*ttl*/,
- void* context) {
- D("Got IP for service.");
+ DNSServiceErrorType errorCode, const char* hostname,
+ const sockaddr* address, uint32_t ttl, void* context) {
+ D("%s: sdRef=%p flags=0x%08x errorCode=%u ttl=%u", __func__, sdRef, flags, errorCode, ttl);
std::unique_ptr<ResolvedService> data(
reinterpret_cast<ResolvedService*>(context));
- data->Connect(address);
+ // Only resolve the address once. If the address or port changes, we'll just get another
+ // registration.
+ data->DestroyServiceRef();
- // For ADB Secure services, keep those ResolvedService's around
- // for later processing with secure connection establishment.
- if (data->serviceIndex() != kADBTransportServiceRefIndex) {
- data.release();
+ if (errorCode != kDNSServiceErr_NoError) {
+ D("Got error while looking up ipaddr [%u]", errorCode);
+ return;
+ }
+
+ if (flags & kDNSServiceFlagsAdd) {
+ D("Resolved IP address for [%s]. Adding to service registry.", hostname);
+ auto* ptr = data.release();
+ if (!ptr->AddToServiceRegistry(address)) {
+ data.reset(ptr);
+ }
}
}
@@ -395,9 +482,13 @@
};
static void adb_RemoveDNSService(const char* regType, const char* serviceName) {
+ D("%s: regType=[%s] serviceName=[%s]", __func__, regType, serviceName);
int index = adb_DNSServiceIndexByName(regType);
ResolvedService::ServiceRegistry* services;
switch (index) {
+ case kADBTransportServiceRefIndex:
+ services = ResolvedService::sAdbTransportServices;
+ break;
case kADBSecurePairingServiceRefIndex:
services = ResolvedService::sAdbSecurePairingServices;
break;
@@ -408,10 +499,15 @@
return;
}
+ if (services->empty()) {
+ return;
+ }
+
std::string sName(serviceName);
- services->erase(std::remove_if(
- services->begin(), services->end(),
- [&sName](ResolvedService* service) { return (sName == service->serviceName()); }));
+ services->erase(std::remove_if(services->begin(), services->end(),
+ [&sName](std::unique_ptr<ResolvedService>& service) {
+ return (sName == service->serviceName());
+ }));
}
// Returns the version the device wanted to advertise,
@@ -521,8 +617,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);
@@ -542,6 +645,110 @@
}
void init_mdns_transport_discovery(void) {
- ResolvedService::initAdbSecure();
+ ResolvedService::initAdbServiceRegistries();
std::thread(init_mdns_transport_discovery_thread).detach();
}
+
+std::string mdns_check() {
+ uint32_t daemon_version;
+ uint32_t sz = sizeof(daemon_version);
+
+ auto dnserr = DNSServiceGetProperty(kDNSServiceProperty_DaemonVersion, &daemon_version, &sz);
+ std::string result = "ERROR: mdns daemon unavailable";
+ if (dnserr != kDNSServiceErr_NoError) {
+ return result;
+ }
+
+ result = android::base::StringPrintf("mdns daemon version [%u]", daemon_version);
+ return result;
+}
+
+std::string mdns_list_discovered_services() {
+ std::string result;
+ auto cb = [&](const char* service_name, const char* reg_type, const char* ip_addr,
+ uint16_t port) {
+ result += android::base::StringPrintf("%s\t%s\t%s:%u\n", service_name, reg_type, ip_addr,
+ port);
+ };
+
+ ResolvedService::forEachService(*ResolvedService::sAdbTransportServices, "", cb);
+ ResolvedService::forEachService(*ResolvedService::sAdbSecureConnectServices, "", cb);
+ ResolvedService::forEachService(*ResolvedService::sAdbSecurePairingServices, "", cb);
+ return result;
+}
+
+std::optional<MdnsInfo> mdns_get_connect_service_info(std::string_view name) {
+ CHECK(!name.empty());
+
+ auto mdns_instance = mdns::mdns_parse_instance_name(name);
+ if (!mdns_instance.has_value()) {
+ D("Failed to parse mDNS name [%s]", name.data());
+ return std::nullopt;
+ }
+
+ std::optional<MdnsInfo> info;
+ auto cb = [&](const char* service_name, const char* reg_type, const char* ip_addr,
+ uint16_t port) { info.emplace(service_name, reg_type, ip_addr, port); };
+
+ std::string reg_type;
+ if (!mdns_instance->service_name.empty()) {
+ reg_type = android::base::StringPrintf("%s.%s", mdns_instance->service_name.data(),
+ mdns_instance->transport_type.data());
+ int index = adb_DNSServiceIndexByName(reg_type);
+ switch (index) {
+ case kADBTransportServiceRefIndex:
+ ResolvedService::forEachService(*ResolvedService::sAdbTransportServices,
+ mdns_instance->instance_name, cb);
+ break;
+ case kADBSecureConnectServiceRefIndex:
+ ResolvedService::forEachService(*ResolvedService::sAdbSecureConnectServices,
+ mdns_instance->instance_name, cb);
+ break;
+ default:
+ D("Unknown reg_type [%s]", reg_type.data());
+ return std::nullopt;
+ }
+ return info;
+ }
+
+ for (const auto& service :
+ {ResolvedService::sAdbTransportServices, ResolvedService::sAdbSecureConnectServices}) {
+ ResolvedService::forEachService(*service, name, cb);
+ if (info.has_value()) {
+ return info;
+ }
+ }
+
+ return std::nullopt;
+}
+
+std::optional<MdnsInfo> mdns_get_pairing_service_info(std::string_view name) {
+ CHECK(!name.empty());
+
+ auto mdns_instance = mdns::mdns_parse_instance_name(name);
+ if (!mdns_instance.has_value()) {
+ D("Failed to parse mDNS pairing name [%s]", name.data());
+ return std::nullopt;
+ }
+
+ std::optional<MdnsInfo> info;
+ auto cb = [&](const char* service_name, const char* reg_type, const char* ip_addr,
+ uint16_t port) { info.emplace(service_name, reg_type, ip_addr, port); };
+
+ // Verify it's a pairing service if user explicitly inputs it.
+ if (!mdns_instance->service_name.empty()) {
+ auto reg_type = android::base::StringPrintf("%s.%s", mdns_instance->service_name.data(),
+ mdns_instance->transport_type.data());
+ int index = adb_DNSServiceIndexByName(reg_type);
+ switch (index) {
+ case kADBSecurePairingServiceRefIndex:
+ break;
+ default:
+ D("Not an adb pairing reg_type [%s]", reg_type.data());
+ return std::nullopt;
+ }
+ }
+
+ ResolvedService::forEachService(*ResolvedService::sAdbSecurePairingServices, name, cb);
+ return info;
+}
diff --git a/adb/transport_usb.cpp b/adb/client/transport_usb.cpp
similarity index 96%
rename from adb/transport_usb.cpp
rename to adb/client/transport_usb.cpp
index fb81b37..777edde 100644
--- a/adb/transport_usb.cpp
+++ b/adb/client/transport_usb.cpp
@@ -16,6 +16,10 @@
#define TRACE_TAG TRANSPORT
+#include "sysdeps.h"
+
+#include "client/usb.h"
+
#include <memory>
#include "sysdeps.h"
@@ -135,8 +139,8 @@
}
p->payload.resize(p->msg.data_length);
- if (usb_read(usb, &p->payload[0], p->payload.size())
- != static_cast<int>(p->payload.size())) {
+ if (usb_read(usb, &p->payload[0], p->payload.size()) !=
+ static_cast<int>(p->payload.size())) {
PLOG(ERROR) << "remote usb: terminated (data)";
return -1;
}
diff --git a/adb/usb.h b/adb/client/usb.h
similarity index 72%
rename from adb/usb.h
rename to adb/client/usb.h
index eb8ca6c..b371788 100644
--- a/adb/usb.h
+++ b/adb/client/usb.h
@@ -18,6 +18,9 @@
#include <sys/types.h>
+#include "adb.h"
+#include "transport.h"
+
// USB host/client interface.
#define ADB_USB_INTERFACE(handle_ref_type) \
@@ -30,35 +33,38 @@
void usb_kick(handle_ref_type h); \
size_t usb_get_max_packet_size(handle_ref_type)
-#if !ADB_HOST
-// The daemon has a single implementation.
-
-struct usb_handle;
-ADB_USB_INTERFACE(usb_handle*);
-
-#else // linux host || darwin
// Linux and Darwin clients have native and libusb implementations.
namespace libusb {
- struct usb_handle;
- ADB_USB_INTERFACE(libusb::usb_handle*);
-}
+struct usb_handle;
+ADB_USB_INTERFACE(libusb::usb_handle*);
+} // namespace libusb
namespace native {
- struct usb_handle;
- ADB_USB_INTERFACE(native::usb_handle*);
-}
+struct usb_handle;
+ADB_USB_INTERFACE(native::usb_handle*);
+} // namespace native
// Empty base that both implementations' opaque handles inherit from.
-struct usb_handle {
-};
+struct usb_handle {};
ADB_USB_INTERFACE(::usb_handle*);
-#endif // linux host || darwin
-
-
// USB device detection.
int is_adb_interface(int usb_class, int usb_subclass, int usb_protocol);
bool should_use_libusb();
+
+struct UsbConnection : public BlockingConnection {
+ explicit UsbConnection(usb_handle* handle) : handle_(handle) {}
+ ~UsbConnection();
+
+ bool Read(apacket* packet) override final;
+ bool Write(apacket* packet) override final;
+ bool DoTlsHandshake(RSA* key, std::string* auth_key) override final;
+
+ void Close() override final;
+ virtual void Reset() override final;
+
+ usb_handle* handle_;
+};
diff --git a/adb/client/usb_dispatch.cpp b/adb/client/usb_dispatch.cpp
index f55ae90..7b97117 100644
--- a/adb/client/usb_dispatch.cpp
+++ b/adb/client/usb_dispatch.cpp
@@ -15,7 +15,8 @@
*/
#include <android-base/logging.h>
-#include "usb.h"
+
+#include "client/usb.h"
void usb_init() {
if (should_use_libusb()) {
diff --git a/adb/client/usb_libusb.cpp b/adb/client/usb_libusb.cpp
index 53f01a0..07cbc94 100644
--- a/adb/client/usb_libusb.cpp
+++ b/adb/client/usb_libusb.cpp
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-#include "usb.h"
-
#include "sysdeps.h"
+#include "client/usb.h"
+
#include <stdint.h>
#include <stdlib.h>
@@ -40,7 +40,6 @@
#include "adb.h"
#include "adb_utils.h"
#include "transport.h"
-#include "usb.h"
using android::base::StringPrintf;
diff --git a/adb/client/usb_linux.cpp b/adb/client/usb_linux.cpp
index 343e7b5..95b1817 100644
--- a/adb/client/usb_linux.cpp
+++ b/adb/client/usb_linux.cpp
@@ -18,6 +18,8 @@
#include "sysdeps.h"
+#include "client/usb.h"
+
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
@@ -48,7 +50,6 @@
#include "adb.h"
#include "transport.h"
-#include "usb.h"
using namespace std::chrono_literals;
using namespace std::literals;
diff --git a/adb/client/usb_osx.cpp b/adb/client/usb_osx.cpp
index 7207ca7..a93fa3a 100644
--- a/adb/client/usb_osx.cpp
+++ b/adb/client/usb_osx.cpp
@@ -18,6 +18,8 @@
#include "sysdeps.h"
+#include "client/usb.h"
+
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
diff --git a/adb/client/usb_windows.cpp b/adb/client/usb_windows.cpp
index 197c6fa..e209230 100644
--- a/adb/client/usb_windows.cpp
+++ b/adb/client/usb_windows.cpp
@@ -18,6 +18,8 @@
#include "sysdeps.h"
+#include "client/usb.h"
+
// clang-format off
#include <winsock2.h> // winsock.h *must* be included before windows.h.
#include <windows.h>
diff --git a/adb/compression_utils.h b/adb/compression_utils.h
new file mode 100644
index 0000000..a747108
--- /dev/null
+++ b/adb/compression_utils.h
@@ -0,0 +1,486 @@
+/*
+ * 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 <algorithm>
+#include <memory>
+#include <span>
+
+#include <android-base/logging.h>
+
+#include <brotli/decode.h>
+#include <brotli/encode.h>
+#include <lz4frame.h>
+#include <zstd.h>
+
+#include "types.h"
+
+enum class DecodeResult {
+ Error,
+ Done,
+ NeedInput,
+ MoreOutput,
+};
+
+enum class EncodeResult {
+ Error,
+ Done,
+ NeedInput,
+ MoreOutput,
+};
+
+struct Decoder {
+ void Append(Block&& block) { input_buffer_.append(std::move(block)); }
+ bool Finish() {
+ bool old = std::exchange(finished_, true);
+ if (old) {
+ LOG(FATAL) << "Decoder::Finish called while already finished?";
+ return false;
+ }
+ return true;
+ }
+
+ virtual DecodeResult Decode(std::span<char>* output) = 0;
+
+ protected:
+ Decoder(std::span<char> output_buffer) : output_buffer_(output_buffer) {}
+ ~Decoder() = default;
+
+ bool finished_ = false;
+ IOVector input_buffer_;
+ std::span<char> output_buffer_;
+};
+
+struct Encoder {
+ void Append(Block input) { input_buffer_.append(std::move(input)); }
+ bool Finish() {
+ bool old = std::exchange(finished_, true);
+ if (old) {
+ LOG(FATAL) << "Decoder::Finish called while already finished?";
+ return false;
+ }
+ return true;
+ }
+
+ virtual EncodeResult Encode(Block* output) = 0;
+
+ protected:
+ explicit Encoder(size_t output_block_size) : output_block_size_(output_block_size) {}
+ ~Encoder() = default;
+
+ const size_t output_block_size_;
+ bool finished_ = false;
+ IOVector input_buffer_;
+};
+
+struct NullDecoder final : public Decoder {
+ explicit NullDecoder(std::span<char> output_buffer) : Decoder(output_buffer) {}
+
+ DecodeResult Decode(std::span<char>* output) final {
+ size_t available_out = output_buffer_.size();
+ void* p = output_buffer_.data();
+ while (available_out > 0 && !input_buffer_.empty()) {
+ size_t len = std::min(available_out, input_buffer_.front_size());
+ p = mempcpy(p, input_buffer_.front_data(), len);
+ available_out -= len;
+ input_buffer_.drop_front(len);
+ }
+ *output = std::span(output_buffer_.data(), static_cast<char*>(p));
+ if (input_buffer_.empty()) {
+ return finished_ ? DecodeResult::Done : DecodeResult::NeedInput;
+ }
+ return DecodeResult::MoreOutput;
+ }
+};
+
+struct NullEncoder final : public Encoder {
+ explicit NullEncoder(size_t output_block_size) : Encoder(output_block_size) {}
+
+ EncodeResult Encode(Block* output) final {
+ output->clear();
+ output->resize(output_block_size_);
+
+ size_t available_out = output->size();
+ void* p = output->data();
+
+ while (available_out > 0 && !input_buffer_.empty()) {
+ size_t len = std::min(available_out, input_buffer_.front_size());
+ p = mempcpy(p, input_buffer_.front_data(), len);
+ available_out -= len;
+ input_buffer_.drop_front(len);
+ }
+
+ output->resize(output->size() - available_out);
+
+ if (input_buffer_.empty()) {
+ return finished_ ? EncodeResult::Done : EncodeResult::NeedInput;
+ }
+ return EncodeResult::MoreOutput;
+ }
+};
+
+struct BrotliDecoder final : public Decoder {
+ explicit BrotliDecoder(std::span<char> output_buffer)
+ : Decoder(output_buffer),
+ decoder_(BrotliDecoderCreateInstance(nullptr, nullptr, nullptr),
+ BrotliDecoderDestroyInstance) {}
+
+ DecodeResult Decode(std::span<char>* output) final {
+ size_t available_in = input_buffer_.front_size();
+ const uint8_t* next_in = reinterpret_cast<const uint8_t*>(input_buffer_.front_data());
+
+ size_t available_out = output_buffer_.size();
+ uint8_t* next_out = reinterpret_cast<uint8_t*>(output_buffer_.data());
+
+ BrotliDecoderResult r = BrotliDecoderDecompressStream(
+ decoder_.get(), &available_in, &next_in, &available_out, &next_out, nullptr);
+
+ size_t bytes_consumed = input_buffer_.front_size() - available_in;
+ input_buffer_.drop_front(bytes_consumed);
+
+ size_t bytes_emitted = output_buffer_.size() - available_out;
+ *output = std::span<char>(output_buffer_.data(), bytes_emitted);
+
+ switch (r) {
+ case BROTLI_DECODER_RESULT_SUCCESS:
+ // We need to wait for ID_DONE from the other end.
+ return finished_ ? DecodeResult::Done : DecodeResult::NeedInput;
+ case BROTLI_DECODER_RESULT_ERROR:
+ return DecodeResult::Error;
+ case BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT:
+ // Brotli guarantees as one of its invariants that if it returns NEEDS_MORE_INPUT,
+ // it will consume the entire input buffer passed in, so we don't have to worry
+ // about bytes left over in the front block with more input remaining.
+ return DecodeResult::NeedInput;
+ case BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT:
+ return DecodeResult::MoreOutput;
+ }
+ }
+
+ private:
+ std::unique_ptr<BrotliDecoderState, void (*)(BrotliDecoderState*)> decoder_;
+};
+
+struct BrotliEncoder final : public Encoder {
+ explicit BrotliEncoder(size_t output_block_size)
+ : Encoder(output_block_size),
+ output_block_(output_block_size_),
+ output_bytes_left_(output_block_size_),
+ encoder_(BrotliEncoderCreateInstance(nullptr, nullptr, nullptr),
+ BrotliEncoderDestroyInstance) {
+ BrotliEncoderSetParameter(encoder_.get(), BROTLI_PARAM_QUALITY, 1);
+ }
+
+ EncodeResult Encode(Block* output) final {
+ output->clear();
+
+ while (true) {
+ size_t available_in = input_buffer_.front_size();
+ const uint8_t* next_in = reinterpret_cast<const uint8_t*>(input_buffer_.front_data());
+
+ size_t available_out = output_bytes_left_;
+ uint8_t* next_out = reinterpret_cast<uint8_t*>(
+ output_block_.data() + (output_block_size_ - output_bytes_left_));
+
+ BrotliEncoderOperation op = BROTLI_OPERATION_PROCESS;
+ if (finished_) {
+ op = BROTLI_OPERATION_FINISH;
+ }
+
+ if (!BrotliEncoderCompressStream(encoder_.get(), op, &available_in, &next_in,
+ &available_out, &next_out, nullptr)) {
+ return EncodeResult::Error;
+ }
+
+ size_t bytes_consumed = input_buffer_.front_size() - available_in;
+ input_buffer_.drop_front(bytes_consumed);
+
+ output_bytes_left_ = available_out;
+
+ if (BrotliEncoderIsFinished(encoder_.get())) {
+ output_block_.resize(output_block_size_ - output_bytes_left_);
+ *output = std::move(output_block_);
+ return EncodeResult::Done;
+ } else if (output_bytes_left_ == 0) {
+ *output = std::move(output_block_);
+ output_block_.resize(output_block_size_);
+ output_bytes_left_ = output_block_size_;
+ return EncodeResult::MoreOutput;
+ } else if (input_buffer_.empty()) {
+ return EncodeResult::NeedInput;
+ }
+ }
+ }
+
+ private:
+ Block output_block_;
+ size_t output_bytes_left_;
+ std::unique_ptr<BrotliEncoderState, void (*)(BrotliEncoderState*)> encoder_;
+};
+
+struct LZ4Decoder final : public Decoder {
+ explicit LZ4Decoder(std::span<char> output_buffer)
+ : Decoder(output_buffer), decoder_(nullptr, nullptr) {
+ LZ4F_dctx* dctx;
+ if (LZ4F_createDecompressionContext(&dctx, LZ4F_VERSION) != 0) {
+ LOG(FATAL) << "failed to initialize LZ4 decompression context";
+ }
+ decoder_ = std::unique_ptr<LZ4F_dctx, decltype(&LZ4F_freeDecompressionContext)>(
+ dctx, LZ4F_freeDecompressionContext);
+ }
+
+ DecodeResult Decode(std::span<char>* output) final {
+ size_t available_in = input_buffer_.front_size();
+ const char* next_in = input_buffer_.front_data();
+
+ size_t available_out = output_buffer_.size();
+ char* next_out = output_buffer_.data();
+
+ size_t rc = LZ4F_decompress(decoder_.get(), next_out, &available_out, next_in,
+ &available_in, nullptr);
+ if (LZ4F_isError(rc)) {
+ LOG(ERROR) << "LZ4F_decompress failed: " << LZ4F_getErrorName(rc);
+ return DecodeResult::Error;
+ }
+
+ input_buffer_.drop_front(available_in);
+
+ if (rc == 0) {
+ if (!input_buffer_.empty()) {
+ LOG(ERROR) << "LZ4 stream hit end before reading all data";
+ return DecodeResult::Error;
+ }
+ lz4_done_ = true;
+ }
+
+ *output = std::span<char>(output_buffer_.data(), available_out);
+
+ if (finished_) {
+ return input_buffer_.empty() && lz4_done_ ? DecodeResult::Done
+ : DecodeResult::MoreOutput;
+ }
+
+ return DecodeResult::NeedInput;
+ }
+
+ private:
+ bool lz4_done_ = false;
+ std::unique_ptr<LZ4F_dctx, LZ4F_errorCode_t (*)(LZ4F_dctx*)> decoder_;
+};
+
+struct LZ4Encoder final : public Encoder {
+ explicit LZ4Encoder(size_t output_block_size)
+ : Encoder(output_block_size), encoder_(nullptr, nullptr) {
+ LZ4F_cctx* cctx;
+ if (LZ4F_createCompressionContext(&cctx, LZ4F_VERSION) != 0) {
+ LOG(FATAL) << "failed to initialize LZ4 compression context";
+ }
+ encoder_ = std::unique_ptr<LZ4F_cctx, decltype(&LZ4F_freeCompressionContext)>(
+ cctx, LZ4F_freeCompressionContext);
+ Block header(LZ4F_HEADER_SIZE_MAX);
+ size_t rc = LZ4F_compressBegin(encoder_.get(), header.data(), header.size(), nullptr);
+ if (LZ4F_isError(rc)) {
+ LOG(FATAL) << "LZ4F_compressBegin failed: %s", LZ4F_getErrorName(rc);
+ }
+ header.resize(rc);
+ output_buffer_.append(std::move(header));
+ }
+
+ // As an optimization, only emit a block if we have an entire output block ready, or we're done.
+ bool OutputReady() const {
+ return output_buffer_.size() >= output_block_size_ || lz4_finalized_;
+ }
+
+ // TODO: Switch the output type to IOVector to remove a copy?
+ EncodeResult Encode(Block* output) final {
+ size_t available_in = input_buffer_.front_size();
+ const char* next_in = input_buffer_.front_data();
+
+ // LZ4 makes no guarantees about being able to recover from trying to compress with an
+ // insufficiently large output buffer. LZ4F_compressBound tells us how much buffer we
+ // need to compress a given number of bytes, but the smallest value seems to be bigger
+ // than SYNC_DATA_MAX, so we need to buffer ourselves.
+
+ // Input size chosen to be a local maximum for LZ4F_compressBound (i.e. the block size).
+ constexpr size_t max_input_size = 65536;
+ const size_t encode_block_size = LZ4F_compressBound(max_input_size, nullptr);
+
+ if (available_in != 0) {
+ if (lz4_finalized_) {
+ LOG(ERROR) << "LZ4Encoder received data after Finish?";
+ return EncodeResult::Error;
+ }
+
+ available_in = std::min(available_in, max_input_size);
+
+ Block encode_block(encode_block_size);
+ size_t available_out = encode_block.capacity();
+ char* next_out = encode_block.data();
+
+ size_t rc = LZ4F_compressUpdate(encoder_.get(), next_out, available_out, next_in,
+ available_in, nullptr);
+ if (LZ4F_isError(rc)) {
+ LOG(ERROR) << "LZ4F_compressUpdate failed: " << LZ4F_getErrorName(rc);
+ return EncodeResult::Error;
+ }
+
+ input_buffer_.drop_front(available_in);
+
+ available_out -= rc;
+ next_out += rc;
+
+ encode_block.resize(encode_block_size - available_out);
+ output_buffer_.append(std::move(encode_block));
+ }
+
+ if (finished_ && !lz4_finalized_) {
+ lz4_finalized_ = true;
+
+ Block final_block(encode_block_size + 4);
+ size_t rc = LZ4F_compressEnd(encoder_.get(), final_block.data(), final_block.size(),
+ nullptr);
+ if (LZ4F_isError(rc)) {
+ LOG(ERROR) << "LZ4F_compressEnd failed: " << LZ4F_getErrorName(rc);
+ return EncodeResult::Error;
+ }
+
+ final_block.resize(rc);
+ output_buffer_.append(std::move(final_block));
+ }
+
+ if (OutputReady()) {
+ size_t len = std::min(output_block_size_, output_buffer_.size());
+ *output = output_buffer_.take_front(len).coalesce();
+ } else {
+ output->clear();
+ }
+
+ if (lz4_finalized_ && output_buffer_.empty()) {
+ return EncodeResult::Done;
+ } else if (OutputReady()) {
+ return EncodeResult::MoreOutput;
+ }
+ return EncodeResult::NeedInput;
+ }
+
+ private:
+ bool lz4_finalized_ = false;
+ std::unique_ptr<LZ4F_cctx, LZ4F_errorCode_t (*)(LZ4F_cctx*)> encoder_;
+ IOVector output_buffer_;
+};
+
+struct ZstdDecoder final : public Decoder {
+ explicit ZstdDecoder(std::span<char> output_buffer)
+ : Decoder(output_buffer), decoder_(ZSTD_createDStream(), ZSTD_freeDStream) {
+ if (!decoder_) {
+ LOG(FATAL) << "failed to initialize Zstd decompression context";
+ }
+ }
+
+ DecodeResult Decode(std::span<char>* output) final {
+ ZSTD_inBuffer in;
+ in.src = input_buffer_.front_data();
+ in.size = input_buffer_.front_size();
+ in.pos = 0;
+
+ ZSTD_outBuffer out;
+ out.dst = output_buffer_.data();
+ // The standard specifies size() as returning size_t, but our current version of
+ // libc++ returns a signed value instead.
+ out.size = static_cast<size_t>(output_buffer_.size());
+ out.pos = 0;
+
+ size_t rc = ZSTD_decompressStream(decoder_.get(), &out, &in);
+ if (ZSTD_isError(rc)) {
+ LOG(ERROR) << "ZSTD_decompressStream failed: " << ZSTD_getErrorName(rc);
+ return DecodeResult::Error;
+ }
+
+ input_buffer_.drop_front(in.pos);
+ if (rc == 0) {
+ if (!input_buffer_.empty()) {
+ LOG(ERROR) << "Zstd stream hit end before reading all data";
+ return DecodeResult::Error;
+ }
+ zstd_done_ = true;
+ }
+
+ *output = std::span<char>(output_buffer_.data(), out.pos);
+
+ if (finished_) {
+ return input_buffer_.empty() && zstd_done_ ? DecodeResult::Done
+ : DecodeResult::MoreOutput;
+ }
+ return DecodeResult::NeedInput;
+ }
+
+ private:
+ bool zstd_done_ = false;
+ std::unique_ptr<ZSTD_DStream, size_t (*)(ZSTD_DStream*)> decoder_;
+};
+
+struct ZstdEncoder final : public Encoder {
+ explicit ZstdEncoder(size_t output_block_size)
+ : Encoder(output_block_size), encoder_(ZSTD_createCStream(), ZSTD_freeCStream) {
+ if (!encoder_) {
+ LOG(FATAL) << "failed to initialize Zstd compression context";
+ }
+ ZSTD_CCtx_setParameter(encoder_.get(), ZSTD_c_compressionLevel, 1);
+ }
+
+ EncodeResult Encode(Block* output) final {
+ ZSTD_inBuffer in;
+ in.src = input_buffer_.front_data();
+ in.size = input_buffer_.front_size();
+ in.pos = 0;
+
+ output->resize(output_block_size_);
+
+ ZSTD_outBuffer out;
+ out.dst = output->data();
+ out.size = static_cast<size_t>(output->size());
+ out.pos = 0;
+
+ ZSTD_EndDirective end_directive = finished_ ? ZSTD_e_end : ZSTD_e_continue;
+ size_t rc = ZSTD_compressStream2(encoder_.get(), &out, &in, end_directive);
+ if (ZSTD_isError(rc)) {
+ LOG(ERROR) << "ZSTD_compressStream2 failed: " << ZSTD_getErrorName(rc);
+ return EncodeResult::Error;
+ }
+
+ input_buffer_.drop_front(in.pos);
+ output->resize(out.pos);
+
+ if (rc == 0) {
+ // Zstd finished flushing its data.
+ if (finished_) {
+ if (!input_buffer_.empty()) {
+ LOG(ERROR) << "ZSTD_compressStream2 finished early";
+ return EncodeResult::Error;
+ }
+ return EncodeResult::Done;
+ } else {
+ return input_buffer_.empty() ? EncodeResult::NeedInput : EncodeResult::MoreOutput;
+ }
+ } else {
+ return EncodeResult::MoreOutput;
+ }
+ }
+
+ private:
+ std::unique_ptr<ZSTD_CStream, size_t (*)(ZSTD_CStream*)> encoder_;
+};
diff --git a/adb/coverage/.gitignore b/adb/coverage/.gitignore
new file mode 100644
index 0000000..b6a2582
--- /dev/null
+++ b/adb/coverage/.gitignore
@@ -0,0 +1,2 @@
+/adbd.profdata
+/report
diff --git a/adb/coverage/gen_coverage.sh b/adb/coverage/gen_coverage.sh
new file mode 100755
index 0000000..43d45f0
--- /dev/null
+++ b/adb/coverage/gen_coverage.sh
@@ -0,0 +1,119 @@
+#!/bin/bash
+
+set -euxo pipefail
+
+OUTPUT_DIR=$(dirname "$0")
+. "$OUTPUT_DIR"/include.sh
+
+TRACEDIR=`mktemp -d`
+
+### Make sure we can connect to the device.
+
+# Get the device's wlan0 address.
+IP_ADDR=$(adb shell ip route get 0.0.0.0 oif wlan0 | sed -En -e 's/.*src (\S+)\s.*/\1/p')
+REMOTE_PORT=5555
+REMOTE=$IP_ADDR:$REMOTE_PORT
+LOCAL_SERIAL=$(adb shell getprop ro.serialno)
+
+# Check that we can connect to it.
+adb disconnect
+
+TRANSPORT_ID=$(adb transport-id)
+adb tcpip $REMOTE_PORT
+adb -t $TRANSPORT_ID wait-for-disconnect
+
+adb connect $REMOTE
+
+REMOTE_FETCHED_SERIAL=$(adb -s $REMOTE shell getprop ro.serialno)
+
+if [[ "$LOCAL_SERIAL" != "$REMOTE_FETCHED_SERIAL" ]]; then
+ echo "Mismatch: local serial = $LOCAL_SERIAL, remote serial = $REMOTE_FETCHED_SERIAL"
+ exit 1
+fi
+
+# Back to USB, and make sure adbd is root.
+adb -s $REMOTE usb
+adb disconnect $REMOTE
+
+adb wait-for-device root
+adb root
+adb wait-for-device
+
+TRANSPORT_ID=$(adb transport-id)
+adb usb
+adb -t $TRANSPORT_ID wait-for-disconnect
+
+adb wait-for-device
+
+### Run the adb unit tests and fetch traces from them.
+mkdir "$TRACEDIR"/test_traces
+adb shell rm -rf /data/local/tmp/adb_coverage
+adb shell mkdir /data/local/tmp/adb_coverage
+
+for TEST in $ADB_TESTS; do
+ adb shell LLVM_PROFILE_FILE=/data/local/tmp/adb_coverage/$TEST.profraw /data/nativetest64/$TEST/$TEST
+ adb pull /data/local/tmp/adb_coverage/$TEST.profraw "$TRACEDIR"/test_traces/
+done
+
+adb pull /data/local/tmp/adb_coverage "$TRACEDIR"/test_traces
+
+# Clear logcat and increase the buffer to something ridiculous so we can fetch the pids of adbd later.
+adb shell logcat -c -G128M
+
+# Turn on extremely verbose logging so as to not count debug logging against us.
+adb shell setprop persist.adb.trace_mask 1
+
+### Run test_device.py over USB.
+TRANSPORT_ID=$(adb transport-id)
+adb shell killall adbd
+adb -t $TRANSPORT_ID wait-for-disconnect
+
+adb wait-for-device shell rm -rf "/data/misc/trace/*" /data/local/tmp/adb_coverage/
+"$OUTPUT_DIR"/../test_device.py
+
+# Do a usb reset to exercise the disconnect code.
+adb_usbreset
+adb wait-for-device
+
+# Dump traces from the currently running adbd.
+adb shell killall -37 adbd
+
+echo Waiting for adbd to finish dumping traces
+sleep 5
+
+# Restart adbd in tcp mode.
+TRANSPORT_ID=$(adb transport-id)
+adb tcpip $REMOTE_PORT
+adb -t $TRANSPORT_ID wait-for-disconnect
+
+adb connect $REMOTE
+adb -s $REMOTE wait-for-device
+
+# Instead of running test_device.py again, which takes forever, do some I/O back and forth instead.
+dd if=/dev/zero bs=1024 count=10240 | adb -s $REMOTE raw sink:10485760
+adb -s $REMOTE raw source:10485760 | dd of=/dev/null bs=1024 count=10240
+
+# Dump traces again.
+adb disconnect $REMOTE
+adb shell killall -37 adbd
+
+echo Waiting for adbd to finish dumping traces
+sleep 5
+
+adb pull /data/misc/trace "$TRACEDIR"/
+echo Pulled traces to $TRACEDIR
+
+# Identify which of the trace files are actually adbd, in case something else exited simultaneously.
+ADBD_PIDS=$(adb shell "logcat -d -s adbd --format=process | grep 'adbd started' | cut -c 3-7 | tr -d ' ' | sort | uniq")
+mkdir "$TRACEDIR"/adbd_traces
+
+adb shell 'setprop persist.adb.trace_mask 0; killall adbd'
+
+IFS=$'\n'
+for PID in $ADBD_PIDS; do
+ cp "$TRACEDIR"/trace/clang-$PID-*.profraw "$TRACEDIR"/adbd_traces 2>/dev/null || true
+done
+unset IFS
+
+### Merge the traces.
+llvm-profdata merge --output="$OUTPUT_DIR"/adbd.profdata "$TRACEDIR"/adbd_traces/* "$TRACEDIR"/test_traces/*
diff --git a/adb/coverage/include.sh b/adb/coverage/include.sh
new file mode 100644
index 0000000..45ebc34
--- /dev/null
+++ b/adb/coverage/include.sh
@@ -0,0 +1,5 @@
+ADB_TESTS="adbd_test adb_crypto_test adb_pairing_auth_test adb_pairing_connection_test adb_tls_connection_test"
+ADB_TEST_BINARIES=""
+for TEST in $ADB_TESTS; do
+ ADB_TEST_BINARIES="--object=$ANDROID_PRODUCT_OUT/data/nativetest64/$TEST/$TEST $ADB_TEST_BINARIES"
+done
diff --git a/adb/coverage/report.sh b/adb/coverage/report.sh
new file mode 100755
index 0000000..257310c
--- /dev/null
+++ b/adb/coverage/report.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+set -euxo pipefail
+
+OUTPUT_DIR=$(realpath $(dirname "$0"))
+. "$OUTPUT_DIR"/include.sh
+
+rm -rf "$OUTPUT_DIR"/report
+
+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 \
+ $ADB_TEST_BINARIES \
+ --show-region-summary=false \
+ --format=html -o "$OUTPUT_DIR"/report
+
+llvm-cov report --instr-profile="$OUTPUT_DIR"/adbd.profdata \
+ $ANDROID_PRODUCT_OUT/apex/com.android.adbd/bin/adbd \
+ /proc/self/cwd/system/core/adb \
+ $ADB_TEST_BINARIES \
+ --show-region-summary=false
diff --git a/adb/coverage/show.sh b/adb/coverage/show.sh
new file mode 100755
index 0000000..3b2faa3
--- /dev/null
+++ b/adb/coverage/show.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+set -euxo pipefail
+
+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 \
+ $PATHS \
+ $ADB_TEST_BINARIES
diff --git a/adb/crypto/Android.bp b/adb/crypto/Android.bp
index ce1de4a..9d14b03 100644
--- a/adb/crypto/Android.bp
+++ b/adb/crypto/Android.bp
@@ -40,6 +40,7 @@
visibility: [
"//system/core/adb:__subpackages__",
+ "//bootable/recovery/minadbd:__subpackages__",
],
host_supported: true,
diff --git a/adb/daemon/file_sync_service.cpp b/adb/daemon/file_sync_service.cpp
index edf5683..513b8dd 100644
--- a/adb/daemon/file_sync_service.cpp
+++ b/adb/daemon/file_sync_service.cpp
@@ -32,7 +32,10 @@
#include <utime.h>
#include <memory>
+#include <optional>
+#include <span>
#include <string>
+#include <variant>
#include <vector>
#include <android-base/file.h>
@@ -55,10 +58,12 @@
#include "adb_io.h"
#include "adb_trace.h"
#include "adb_utils.h"
+#include "compression_utils.h"
#include "file_sync_protocol.h"
#include "security_log_tags.h"
#include "sysdeps/errno.h"
+using android::base::borrowed_fd;
using android::base::Dirname;
using android::base::StringPrintf;
@@ -249,7 +254,7 @@
// Make sure that SendFail from adb_io.cpp isn't accidentally used in this file.
#pragma GCC poison SendFail
-static bool SendSyncFail(int fd, const std::string& reason) {
+static bool SendSyncFail(borrowed_fd fd, const std::string& reason) {
D("sync: failure: %s", reason.c_str());
syncmsg msg;
@@ -258,79 +263,136 @@
return WriteFdExactly(fd, &msg.data, sizeof(msg.data)) && WriteFdExactly(fd, reason);
}
-static bool SendSyncFailErrno(int fd, const std::string& reason) {
+static bool SendSyncFailErrno(borrowed_fd fd, const std::string& reason) {
return SendSyncFail(fd, StringPrintf("%s: %s", reason.c_str(), strerror(errno)));
}
-static bool handle_send_file(int s, const char* path, uint32_t* timestamp, uid_t uid, gid_t gid,
- uint64_t capabilities, mode_t mode, std::vector<char>& buffer,
- bool do_unlink) {
- int rc;
+static bool handle_send_file_data(borrowed_fd s, unique_fd fd, uint32_t* timestamp,
+ CompressionType compression) {
syncmsg msg;
+ Block buffer(SYNC_DATA_MAX);
+ std::span<char> buffer_span(buffer.data(), buffer.size());
+ std::variant<std::monostate, NullDecoder, BrotliDecoder, LZ4Decoder, ZstdDecoder>
+ decoder_storage;
+ Decoder* decoder = nullptr;
- __android_log_security_bswrite(SEC_TAG_ADB_SEND_FILE, path);
+ switch (compression) {
+ case CompressionType::None:
+ decoder = &decoder_storage.emplace<NullDecoder>(buffer_span);
+ break;
- unique_fd fd(adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode));
+ case CompressionType::Brotli:
+ decoder = &decoder_storage.emplace<BrotliDecoder>(buffer_span);
+ break;
- if (fd < 0 && errno == ENOENT) {
- if (!secure_mkdirs(Dirname(path))) {
- SendSyncFailErrno(s, "secure_mkdirs failed");
- goto fail;
- }
- fd.reset(adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode));
- }
- if (fd < 0 && errno == EEXIST) {
- fd.reset(adb_open_mode(path, O_WRONLY | O_CLOEXEC, mode));
- }
- if (fd < 0) {
- SendSyncFailErrno(s, "couldn't create file");
- goto fail;
- } else {
- if (fchown(fd.get(), uid, gid) == -1) {
- SendSyncFailErrno(s, "fchown failed");
- goto fail;
- }
+ case CompressionType::LZ4:
+ decoder = &decoder_storage.emplace<LZ4Decoder>(buffer_span);
+ break;
-#if defined(__ANDROID__)
- // Not all filesystems support setting SELinux labels. http://b/23530370.
- selinux_android_restorecon(path, 0);
-#endif
+ case CompressionType::Zstd:
+ decoder = &decoder_storage.emplace<ZstdDecoder>(buffer_span);
+ break;
- // fchown clears the setuid bit - restore it if present.
- // Ignore the result of calling fchmod. It's not supported
- // by all filesystems, so we don't check for success. b/12441485
- fchmod(fd.get(), mode);
- }
-
- rc = posix_fadvise(fd.get(), 0, 0,
- POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE | POSIX_FADV_WILLNEED);
- if (rc != 0) {
- D("[ Failed to fadvise: %s ]", strerror(rc));
+ case CompressionType::Any:
+ LOG(FATAL) << "unexpected CompressionType::Any";
}
while (true) {
- if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) goto fail;
+ if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) return false;
- if (msg.data.id != ID_DATA) {
- if (msg.data.id == ID_DONE) {
- *timestamp = msg.data.size;
- break;
- }
+ if (msg.data.id == ID_DONE) {
+ *timestamp = msg.data.size;
+ decoder->Finish();
+ } else if (msg.data.id == ID_DATA) {
+ Block block(msg.data.size);
+ if (!ReadFdExactly(s, block.data(), msg.data.size)) return false;
+ decoder->Append(std::move(block));
+ } else {
SendSyncFail(s, "invalid data message");
- goto abort;
+ return false;
}
- if (msg.data.size > buffer.size()) { // TODO: resize buffer?
- SendSyncFail(s, "oversize data message");
- goto abort;
+ while (true) {
+ std::span<char> output;
+ DecodeResult result = decoder->Decode(&output);
+ if (result == DecodeResult::Error) {
+ SendSyncFailErrno(s, "decompress failed");
+ return false;
+ }
+
+ // fd is -1 if the client is pushing with --dry-run.
+ if (fd != -1) {
+ if (!WriteFdExactly(fd, output.data(), output.size())) {
+ SendSyncFailErrno(s, "write failed");
+ return false;
+ }
+ }
+
+ if (result == DecodeResult::NeedInput) {
+ break;
+ } else if (result == DecodeResult::MoreOutput) {
+ continue;
+ } else if (result == DecodeResult::Done) {
+ return true;
+ } else {
+ LOG(FATAL) << "invalid DecodeResult: " << static_cast<int>(result);
+ }
}
+ }
- if (!ReadFdExactly(s, &buffer[0], msg.data.size)) goto abort;
+ __builtin_unreachable();
+}
- if (!WriteFdExactly(fd.get(), &buffer[0], msg.data.size)) {
- SendSyncFailErrno(s, "write failed");
+static bool handle_send_file(borrowed_fd s, const char* path, uint32_t* timestamp, uid_t uid,
+ gid_t gid, uint64_t capabilities, mode_t mode,
+ CompressionType compression, bool dry_run, std::vector<char>& buffer,
+ bool do_unlink) {
+ syncmsg msg;
+ unique_fd fd;
+
+ if (!dry_run) {
+ __android_log_security_bswrite(SEC_TAG_ADB_SEND_FILE, path);
+ fd.reset(adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode));
+
+ if (fd < 0 && errno == ENOENT) {
+ if (!secure_mkdirs(Dirname(path))) {
+ SendSyncFailErrno(s, "secure_mkdirs failed");
+ goto fail;
+ }
+ fd.reset(adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode));
+ }
+ if (fd < 0 && errno == EEXIST) {
+ fd.reset(adb_open_mode(path, O_WRONLY | O_CLOEXEC, mode));
+ }
+ if (fd < 0) {
+ SendSyncFailErrno(s, "couldn't create file");
goto fail;
+ } else {
+ if (fchown(fd.get(), uid, gid) == -1) {
+ SendSyncFailErrno(s, "fchown failed");
+ goto fail;
+ }
+
+#if defined(__ANDROID__)
+ // Not all filesystems support setting SELinux labels. http://b/23530370.
+ selinux_android_restorecon(path, 0);
+#endif
+
+ // fchown clears the setuid bit - restore it if present.
+ // Ignore the result of calling fchmod. It's not supported
+ // by all filesystems, so we don't check for success. b/12441485
+ fchmod(fd.get(), mode);
}
+
+ int rc = posix_fadvise(fd.get(), 0, 0,
+ POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE | POSIX_FADV_WILLNEED);
+ if (rc != 0) {
+ D("[ Failed to fadvise: %s ]", strerror(rc));
+ }
+ }
+
+ if (!handle_send_file_data(s, std::move(fd), timestamp, compression)) {
+ goto fail;
}
if (!update_capabilities(path, capabilities)) {
@@ -371,7 +433,6 @@
if (!ReadFdExactly(s, &buffer[0], msg.data.size)) break;
}
-abort:
if (do_unlink) adb_unlink(path);
return false;
}
@@ -381,7 +442,7 @@
uint32_t* timestamp, std::vector<char>& buffer)
__attribute__((error("no symlinks on Windows")));
#else
-static bool handle_send_link(int s, const std::string& path, uint32_t* timestamp,
+static bool handle_send_link(int s, const std::string& path, uint32_t* timestamp, bool dry_run,
std::vector<char>& buffer) {
syncmsg msg;
@@ -400,19 +461,21 @@
if (!ReadFdExactly(s, &buffer[0], len)) return false;
std::string buf_link;
- if (!android::base::Readlink(path, &buf_link) || (buf_link != &buffer[0])) {
- adb_unlink(path.c_str());
- auto ret = symlink(&buffer[0], path.c_str());
- if (ret && errno == ENOENT) {
- if (!secure_mkdirs(Dirname(path))) {
- SendSyncFailErrno(s, "secure_mkdirs failed");
+ if (!dry_run) {
+ if (!android::base::Readlink(path, &buf_link) || (buf_link != &buffer[0])) {
+ adb_unlink(path.c_str());
+ auto ret = symlink(&buffer[0], path.c_str());
+ if (ret && errno == ENOENT) {
+ if (!secure_mkdirs(Dirname(path))) {
+ SendSyncFailErrno(s, "secure_mkdirs failed");
+ return false;
+ }
+ ret = symlink(&buffer[0], path.c_str());
+ }
+ if (ret) {
+ SendSyncFailErrno(s, "symlink failed");
return false;
}
- ret = symlink(&buffer[0], path.c_str());
- }
- if (ret) {
- SendSyncFailErrno(s, "symlink failed");
- return false;
}
}
@@ -432,27 +495,15 @@
}
#endif
-static bool do_send(int s, const std::string& spec, std::vector<char>& buffer) {
- // 'spec' is of the form "/some/path,0755". Break it up.
- size_t comma = spec.find_last_of(',');
- if (comma == std::string::npos) {
- SendSyncFail(s, "missing , in ID_SEND");
- return false;
- }
-
- std::string path = spec.substr(0, comma);
-
- errno = 0;
- mode_t mode = strtoul(spec.substr(comma + 1).c_str(), nullptr, 0);
- if (errno != 0) {
- SendSyncFail(s, "bad mode");
- return false;
- }
-
+static bool send_impl(int s, const std::string& path, mode_t mode, CompressionType compression,
+ bool dry_run, std::vector<char>& buffer) {
// Don't delete files before copying if they are not "regular" or symlinks.
struct stat st;
- bool do_unlink = (lstat(path.c_str(), &st) == -1) || S_ISREG(st.st_mode) ||
- (S_ISLNK(st.st_mode) && !S_ISLNK(mode));
+ bool do_unlink = false;
+ if (!dry_run) {
+ do_unlink = (lstat(path.c_str(), &st) == -1) || S_ISREG(st.st_mode) ||
+ (S_ISLNK(st.st_mode) && !S_ISLNK(mode));
+ }
if (do_unlink) {
adb_unlink(path.c_str());
}
@@ -460,7 +511,7 @@
bool result;
uint32_t timestamp;
if (S_ISLNK(mode)) {
- result = handle_send_link(s, path, ×tamp, buffer);
+ result = handle_send_link(s, path, ×tamp, dry_run, buffer);
} else {
// Copy user permission bits to "group" and "other" permissions.
mode &= 0777;
@@ -470,12 +521,12 @@
uid_t uid = -1;
gid_t gid = -1;
uint64_t capabilities = 0;
- if (should_use_fs_config(path)) {
+ if (should_use_fs_config(path) && !dry_run) {
adbd_fs_config(path.c_str(), 0, nullptr, &uid, &gid, &mode, &capabilities);
}
- result = handle_send_file(s, path.c_str(), ×tamp, uid, gid, capabilities, mode, buffer,
- do_unlink);
+ result = handle_send_file(s, path.c_str(), ×tamp, uid, gid, capabilities, mode,
+ compression, dry_run, buffer, do_unlink);
}
if (!result) {
@@ -491,7 +542,85 @@
return true;
}
-static bool do_recv(int s, const char* path, std::vector<char>& buffer) {
+static bool do_send_v1(int s, const std::string& spec, std::vector<char>& buffer) {
+ // 'spec' is of the form "/some/path,0755". Break it up.
+ size_t comma = spec.find_last_of(',');
+ if (comma == std::string::npos) {
+ SendSyncFail(s, "missing , in ID_SEND_V1");
+ return false;
+ }
+
+ std::string path = spec.substr(0, comma);
+
+ errno = 0;
+ mode_t mode = strtoul(spec.substr(comma + 1).c_str(), nullptr, 0);
+ if (errno != 0) {
+ SendSyncFail(s, "bad mode");
+ return false;
+ }
+
+ return send_impl(s, path, mode, CompressionType::None, false, buffer);
+}
+
+static bool do_send_v2(int s, const std::string& path, std::vector<char>& buffer) {
+ // Read the setup packet.
+ syncmsg msg;
+ int rc = ReadFdExactly(s, &msg.send_v2_setup, sizeof(msg.send_v2_setup));
+ if (rc == 0) {
+ LOG(ERROR) << "failed to read send_v2 setup packet: EOF";
+ return false;
+ } else if (rc < 0) {
+ PLOG(ERROR) << "failed to read send_v2 setup packet";
+ }
+
+ bool dry_run = false;
+ std::optional<CompressionType> compression;
+
+ uint32_t orig_flags = msg.send_v2_setup.flags;
+ if (msg.send_v2_setup.flags & kSyncFlagBrotli) {
+ msg.send_v2_setup.flags &= ~kSyncFlagBrotli;
+ if (compression) {
+ SendSyncFail(s, android::base::StringPrintf("multiple compression flags received: %d",
+ orig_flags));
+ return false;
+ }
+ compression = CompressionType::Brotli;
+ }
+ if (msg.send_v2_setup.flags & kSyncFlagLZ4) {
+ msg.send_v2_setup.flags &= ~kSyncFlagLZ4;
+ if (compression) {
+ SendSyncFail(s, android::base::StringPrintf("multiple compression flags received: %d",
+ orig_flags));
+ return false;
+ }
+ compression = CompressionType::LZ4;
+ }
+ if (msg.send_v2_setup.flags & kSyncFlagZstd) {
+ msg.send_v2_setup.flags &= ~kSyncFlagZstd;
+ if (compression) {
+ SendSyncFail(s, android::base::StringPrintf("multiple compression flags received: %d",
+ orig_flags));
+ return false;
+ }
+ compression = CompressionType::Zstd;
+ }
+ if (msg.send_v2_setup.flags & kSyncFlagDryRun) {
+ msg.send_v2_setup.flags &= ~kSyncFlagDryRun;
+ dry_run = true;
+ }
+
+ if (msg.send_v2_setup.flags) {
+ SendSyncFail(s, android::base::StringPrintf("unknown flags: %d", msg.send_v2_setup.flags));
+ return false;
+ }
+
+ errno = 0;
+ return send_impl(s, path, msg.send_v2_setup.mode, compression.value_or(CompressionType::None),
+ dry_run, buffer);
+}
+
+static bool recv_impl(borrowed_fd s, const char* path, CompressionType compression,
+ std::vector<char>& buffer) {
__android_log_security_bswrite(SEC_TAG_ADB_RECV_FILE, path);
unique_fd fd(adb_open(path, O_RDONLY | O_CLOEXEC));
@@ -507,16 +636,72 @@
syncmsg msg;
msg.data.id = ID_DATA;
- while (true) {
- int r = adb_read(fd.get(), &buffer[0], buffer.size() - sizeof(msg.data));
- if (r <= 0) {
- if (r == 0) break;
+
+ std::variant<std::monostate, NullEncoder, BrotliEncoder, LZ4Encoder, ZstdEncoder>
+ encoder_storage;
+ Encoder* encoder;
+
+ switch (compression) {
+ case CompressionType::None:
+ encoder = &encoder_storage.emplace<NullEncoder>(SYNC_DATA_MAX);
+ break;
+
+ case CompressionType::Brotli:
+ encoder = &encoder_storage.emplace<BrotliEncoder>(SYNC_DATA_MAX);
+ break;
+
+ case CompressionType::LZ4:
+ encoder = &encoder_storage.emplace<LZ4Encoder>(SYNC_DATA_MAX);
+ break;
+
+ case CompressionType::Zstd:
+ encoder = &encoder_storage.emplace<ZstdEncoder>(SYNC_DATA_MAX);
+ break;
+
+ case CompressionType::Any:
+ LOG(FATAL) << "unexpected CompressionType::Any";
+ }
+
+ bool sending = true;
+ while (sending) {
+ Block input(SYNC_DATA_MAX);
+ int r = adb_read(fd.get(), input.data(), input.size());
+ if (r < 0) {
SendSyncFailErrno(s, "read failed");
return false;
}
- msg.data.size = r;
- if (!WriteFdExactly(s, &msg.data, sizeof(msg.data)) || !WriteFdExactly(s, &buffer[0], r)) {
- return false;
+
+ if (r == 0) {
+ encoder->Finish();
+ } else {
+ input.resize(r);
+ encoder->Append(std::move(input));
+ }
+
+ while (true) {
+ Block output;
+ EncodeResult result = encoder->Encode(&output);
+ if (result == EncodeResult::Error) {
+ SendSyncFailErrno(s, "compress failed");
+ return false;
+ }
+
+ if (!output.empty()) {
+ msg.data.size = output.size();
+ if (!WriteFdExactly(s, &msg.data, sizeof(msg.data)) ||
+ !WriteFdExactly(s, output.data(), output.size())) {
+ return false;
+ }
+ }
+
+ if (result == EncodeResult::Done) {
+ sending = false;
+ break;
+ } else if (result == EncodeResult::NeedInput) {
+ break;
+ } else if (result == EncodeResult::MoreOutput) {
+ continue;
+ }
}
}
@@ -525,6 +710,59 @@
return WriteFdExactly(s, &msg.data, sizeof(msg.data));
}
+static bool do_recv_v1(borrowed_fd s, const char* path, std::vector<char>& buffer) {
+ return recv_impl(s, path, CompressionType::None, buffer);
+}
+
+static bool do_recv_v2(borrowed_fd s, const char* path, std::vector<char>& buffer) {
+ syncmsg msg;
+ // Read the setup packet.
+ int rc = ReadFdExactly(s, &msg.recv_v2_setup, sizeof(msg.recv_v2_setup));
+ if (rc == 0) {
+ LOG(ERROR) << "failed to read recv_v2 setup packet: EOF";
+ return false;
+ } else if (rc < 0) {
+ PLOG(ERROR) << "failed to read recv_v2 setup packet";
+ }
+
+ std::optional<CompressionType> compression;
+ uint32_t orig_flags = msg.recv_v2_setup.flags;
+ if (msg.recv_v2_setup.flags & kSyncFlagBrotli) {
+ msg.recv_v2_setup.flags &= ~kSyncFlagBrotli;
+ if (compression) {
+ SendSyncFail(s, android::base::StringPrintf("multiple compression flags received: %d",
+ orig_flags));
+ return false;
+ }
+ compression = CompressionType::Brotli;
+ }
+ if (msg.recv_v2_setup.flags & kSyncFlagLZ4) {
+ msg.recv_v2_setup.flags &= ~kSyncFlagLZ4;
+ if (compression) {
+ SendSyncFail(s, android::base::StringPrintf("multiple compression flags received: %d",
+ orig_flags));
+ return false;
+ }
+ compression = CompressionType::LZ4;
+ }
+ if (msg.recv_v2_setup.flags & kSyncFlagZstd) {
+ msg.recv_v2_setup.flags &= ~kSyncFlagZstd;
+ if (compression) {
+ SendSyncFail(s, android::base::StringPrintf("multiple compression flags received: %d",
+ orig_flags));
+ return false;
+ }
+ compression = CompressionType::Zstd;
+ }
+
+ if (msg.recv_v2_setup.flags) {
+ SendSyncFail(s, android::base::StringPrintf("unknown flags: %d", msg.recv_v2_setup.flags));
+ return false;
+ }
+
+ return recv_impl(s, path, compression.value_or(CompressionType::None), buffer);
+}
+
static const char* sync_id_to_name(uint32_t id) {
switch (id) {
case ID_LSTAT_V1:
@@ -537,10 +775,14 @@
return "list_v1";
case ID_LIST_V2:
return "list_v2";
- case ID_SEND:
- return "send";
- case ID_RECV:
- return "recv";
+ case ID_SEND_V1:
+ return "send_v1";
+ case ID_SEND_V2:
+ return "send_v2";
+ case ID_RECV_V1:
+ return "recv_v1";
+ case ID_RECV_V2:
+ return "recv_v2";
case ID_QUIT:
return "quit";
default:
@@ -585,11 +827,17 @@
case ID_LIST_V2:
if (!do_list_v2(fd, name)) return false;
break;
- case ID_SEND:
- if (!do_send(fd, name, buffer)) return false;
+ case ID_SEND_V1:
+ if (!do_send_v1(fd, name, buffer)) return false;
break;
- case ID_RECV:
- if (!do_recv(fd, name, buffer)) return false;
+ case ID_SEND_V2:
+ if (!do_send_v2(fd, name, buffer)) return false;
+ break;
+ case ID_RECV_V1:
+ if (!do_recv_v1(fd, name, buffer)) return false;
+ break;
+ case ID_RECV_V2:
+ if (!do_recv_v2(fd, name, buffer)) return false;
break;
case ID_QUIT:
return false;
diff --git a/adb/daemon/jdwp_service.cpp b/adb/daemon/jdwp_service.cpp
index b92a7de..adae9f7 100644
--- a/adb/daemon/jdwp_service.cpp
+++ b/adb/daemon/jdwp_service.cpp
@@ -16,11 +16,13 @@
#if !ADB_HOST
+#if !defined(__ANDROID_RECOVERY__)
#define TRACE_TAG JDWP
#include "sysdeps.h"
#include <errno.h>
+#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -33,6 +35,7 @@
#include <thread>
#include <vector>
+#include <adbconnection/process_info.h>
#include <adbconnection/server.h>
#include <android-base/cmsg.h>
#include <android-base/unique_fd.h>
@@ -41,6 +44,7 @@
#include "adb_io.h"
#include "adb_unique_fd.h"
#include "adb_utils.h"
+#include "app_processes.pb.h"
using android::base::borrowed_fd;
using android::base::unique_fd;
@@ -132,18 +136,24 @@
** for each JDWP process, we record its pid and its connected socket
**/
+enum class TrackerKind {
+ kJdwp,
+ kApp,
+};
+
static void jdwp_process_event(int socket, unsigned events, void* _proc);
static void jdwp_process_list_updated(void);
+static void app_process_list_updated(void);
struct JdwpProcess;
static auto& _jdwp_list = *new std::list<std::unique_ptr<JdwpProcess>>();
struct JdwpProcess {
- JdwpProcess(unique_fd socket, pid_t pid) {
- CHECK(pid != 0);
+ JdwpProcess(unique_fd socket, ProcessInfo process) {
+ CHECK(process.pid != 0);
this->socket = socket;
- this->pid = pid;
+ this->process = process;
this->fde = fdevent_create(socket.release(), jdwp_process_event, this);
if (!this->fde) {
@@ -171,17 +181,19 @@
}
borrowed_fd socket = -1;
- int32_t pid = -1;
+ ProcessInfo process;
fdevent* fde = nullptr;
std::vector<unique_fd> out_fds;
};
+// Populate the list of processes for "track-jdwp" service.
static size_t jdwp_process_list(char* buffer, size_t bufferlen) {
std::string temp;
for (auto& proc : _jdwp_list) {
- std::string next = std::to_string(proc->pid) + "\n";
+ if (!proc->process.debuggable) continue;
+ std::string next = std::to_string(proc->process.pid) + "\n";
if (temp.length() + next.length() > bufferlen) {
D("truncating JDWP process list (max len = %zu)", bufferlen);
break;
@@ -193,7 +205,44 @@
return temp.length();
}
-static size_t jdwp_process_list_msg(char* buffer, size_t bufferlen) {
+// Populate the list of processes for "track-app" service.
+// The list is a protobuf message in the binary format for efficiency.
+static size_t app_process_list(char* buffer, size_t bufferlen) {
+ adb::proto::AppProcesses output; // result that's guaranteed to fit in the given buffer
+ adb::proto::AppProcesses temp; // temporary result that may be longer than the given buffer
+ std::string serialized_message;
+
+ for (auto& proc : _jdwp_list) {
+ if (!proc->process.debuggable && !proc->process.profileable) continue;
+ auto* entry = temp.add_process();
+ entry->set_pid(proc->process.pid);
+ entry->set_debuggable(proc->process.debuggable);
+ entry->set_profileable(proc->process.profileable);
+ entry->set_architecture(proc->process.arch_name, proc->process.arch_name_length);
+ temp.SerializeToString(&serialized_message);
+ if (serialized_message.size() > bufferlen) {
+ D("truncating app process list (max len = %zu)", bufferlen);
+ break;
+ }
+ output = temp;
+ }
+ output.SerializeToString(&serialized_message);
+ memcpy(buffer, serialized_message.data(), serialized_message.length());
+ return serialized_message.length();
+}
+
+// Populate the list of processes for either "track-jdwp" or "track-app" services,
+// depending on the given kind.
+static size_t process_list(TrackerKind kind, char* buffer, size_t bufferlen) {
+ switch (kind) {
+ case TrackerKind::kJdwp:
+ return jdwp_process_list(buffer, bufferlen);
+ case TrackerKind::kApp:
+ return app_process_list(buffer, bufferlen);
+ }
+}
+
+static size_t process_list_msg(TrackerKind kind, char* buffer, size_t bufferlen) {
// Message is length-prefixed with 4 hex digits in ASCII.
static constexpr size_t header_len = 4;
if (bufferlen < header_len) {
@@ -201,7 +250,7 @@
}
char head[header_len + 1];
- size_t len = jdwp_process_list(buffer + header_len, bufferlen - header_len);
+ size_t len = process_list(kind, buffer + header_len, bufferlen - header_len);
snprintf(head, sizeof head, "%04zx", len);
memcpy(buffer, head, header_len);
return len + header_len;
@@ -213,7 +262,7 @@
if (events & FDE_READ) {
// We already have the PID, if we can read from the socket, we've probably hit EOF.
- D("terminating JDWP connection %d", proc->pid);
+ D("terminating JDWP connection %" PRId64, proc->process.pid);
goto CloseProcess;
}
@@ -223,11 +272,12 @@
int fd = proc->out_fds.back().get();
if (android::base::SendFileDescriptors(socket, "", 1, fd) != 1) {
- D("sending new file descriptor to JDWP %d failed: %s", proc->pid, strerror(errno));
+ D("sending new file descriptor to JDWP %" PRId64 " failed: %s", proc->process.pid,
+ strerror(errno));
goto CloseProcess;
}
- D("sent file descriptor %d to JDWP process %d", fd, proc->pid);
+ D("sent file descriptor %d to JDWP process %" PRId64, fd, proc->process.pid);
proc->out_fds.pop_back();
if (proc->out_fds.empty()) {
@@ -238,15 +288,20 @@
return;
CloseProcess:
+ bool debuggable = proc->process.debuggable;
+ bool profileable = proc->process.profileable;
proc->RemoveFromList();
- jdwp_process_list_updated();
+ if (debuggable) jdwp_process_list_updated();
+ if (debuggable || profileable) app_process_list_updated();
}
unique_fd create_jdwp_connection_fd(int pid) {
D("looking for pid %d in JDWP process list", pid);
for (auto& proc : _jdwp_list) {
- if (proc->pid == pid) {
+ // Don't allow JDWP connection to a non-debuggable process.
+ if (!proc->process.debuggable) continue;
+ if (proc->process.pid == static_cast<uint64_t>(pid)) {
int fds[2];
if (adb_socketpair(fds) < 0) {
@@ -338,18 +393,22 @@
**/
struct JdwpTracker : public asocket {
+ TrackerKind kind;
bool need_initial;
+
+ explicit JdwpTracker(TrackerKind k, bool initial) : kind(k), need_initial(initial) {}
};
static auto& _jdwp_trackers = *new std::vector<std::unique_ptr<JdwpTracker>>();
-static void jdwp_process_list_updated(void) {
+static void process_list_updated(TrackerKind kind) {
std::string data;
- data.resize(1024);
- data.resize(jdwp_process_list_msg(&data[0], data.size()));
+ const int kMaxLength = kind == TrackerKind::kJdwp ? 1024 : 2048;
+ data.resize(kMaxLength);
+ data.resize(process_list_msg(kind, &data[0], data.size()));
for (auto& t : _jdwp_trackers) {
- if (t->peer) {
+ if (t->kind == kind && t->peer) {
// The tracker might not have been connected yet.
apacket::payload_type payload(data.begin(), data.end());
t->peer->enqueue(t->peer, std::move(payload));
@@ -357,6 +416,14 @@
}
}
+static void jdwp_process_list_updated(void) {
+ process_list_updated(TrackerKind::kJdwp);
+}
+
+static void app_process_list_updated(void) {
+ process_list_updated(TrackerKind::kApp);
+}
+
static void jdwp_tracker_close(asocket* s) {
D("LS(%d): destroying jdwp tracker service", s->id);
@@ -380,7 +447,7 @@
if (t->need_initial) {
apacket::payload_type data;
data.resize(s->get_max_payload());
- data.resize(jdwp_process_list_msg(&data[0], data.size()));
+ data.resize(process_list_msg(t->kind, &data[0], data.size()));
t->need_initial = false;
s->peer->enqueue(s->peer, std::move(data));
}
@@ -393,8 +460,8 @@
return -1;
}
-asocket* create_jdwp_tracker_service_socket(void) {
- auto t = std::make_unique<JdwpTracker>();
+static asocket* create_process_tracker_service_socket(TrackerKind kind) {
+ auto t = std::make_unique<JdwpTracker>(kind, true);
if (!t) {
LOG(FATAL) << "failed to allocate JdwpTracker";
}
@@ -407,7 +474,6 @@
t->ready = jdwp_tracker_ready;
t->enqueue = jdwp_tracker_enqueue;
t->close = jdwp_tracker_close;
- t->need_initial = true;
asocket* result = t.get();
@@ -416,23 +482,56 @@
return result;
}
+asocket* create_jdwp_tracker_service_socket() {
+ return create_process_tracker_service_socket(TrackerKind::kJdwp);
+}
+
+asocket* create_app_tracker_service_socket() {
+ return create_process_tracker_service_socket(TrackerKind::kApp);
+}
+
int init_jdwp(void) {
std::thread([]() {
adb_thread_setname("jdwp control");
- adbconnection_listen([](int fd, pid_t pid) {
- LOG(INFO) << "jdwp connection from " << pid;
- fdevent_run_on_main_thread([fd, pid] {
+ adbconnection_listen([](int fd, ProcessInfo process) {
+ LOG(INFO) << "jdwp connection from " << process.pid;
+ fdevent_run_on_main_thread([fd, process] {
unique_fd ufd(fd);
- auto proc = std::make_unique<JdwpProcess>(std::move(ufd), pid);
+ auto proc = std::make_unique<JdwpProcess>(std::move(ufd), process);
if (!proc) {
LOG(FATAL) << "failed to allocate JdwpProcess";
}
_jdwp_list.emplace_back(std::move(proc));
- jdwp_process_list_updated();
+ if (process.debuggable) jdwp_process_list_updated();
+ if (process.debuggable || process.profileable) app_process_list_updated();
});
});
}).detach();
return 0;
}
+#else // !defined(__ANDROID_RECOVERY)
+#include "adb.h"
+
+asocket* create_jdwp_service_socket(void) {
+ return nullptr;
+}
+
+unique_fd create_jdwp_connection_fd(int pid) {
+ return {};
+}
+
+asocket* create_app_tracker_service_socket() {
+ return nullptr;
+}
+
+asocket* create_jdwp_tracker_service_socket() {
+ return nullptr;
+}
+
+int init_jdwp() {
+ return 0;
+}
+
+#endif /* defined(__ANDROID_RECOVERY__) */
#endif /* !ADB_HOST */
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index 9e02e89..db8f07b 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -173,12 +173,6 @@
LOG(FATAL) << "Could not set SELinux context";
}
}
- std::string error;
- std::string local_name =
- android::base::StringPrintf("tcp:%d", server_port);
- if (install_listener(local_name, "*smartsocket*", nullptr, 0, nullptr, &error)) {
- LOG(FATAL) << "Could not install *smartsocket* listener: " << error;
- }
}
}
#endif
@@ -301,6 +295,8 @@
setup_adb(addrs);
}
+ LOG(INFO) << "adbd started";
+
D("adbd_main(): pre init_jdwp()");
init_jdwp();
D("adbd_main(): post init_jdwp()");
diff --git a/adb/daemon/mdns.cpp b/adb/daemon/mdns.cpp
index fa692c0..c1e766e 100644
--- a/adb/daemon/mdns.cpp
+++ b/adb/daemon/mdns.cpp
@@ -150,11 +150,11 @@
for (size_t i = 0; i < len; ++i) {
uint8_t val = dist(mt);
if (val < 10) {
- ret += '0' + val;
+ ret += static_cast<char>('0' + val);
} else if (val < 36) {
- ret += 'A' + (val - 10);
+ ret += static_cast<char>('A' + (val - 10));
} else {
- ret += 'a' + (val - 36);
+ ret += static_cast<char>('a' + (val - 36));
}
}
return ret;
diff --git a/adb/daemon/services.cpp b/adb/daemon/services.cpp
index 6bbf66e..a9d1fe8 100644
--- a/adb/daemon/services.cpp
+++ b/adb/daemon/services.cpp
@@ -241,6 +241,8 @@
return create_jdwp_service_socket();
} else if (name == "track-jdwp") {
return create_jdwp_tracker_service_socket();
+ } else if (name == "track-app") {
+ return create_app_tracker_service_socket();
} else if (android::base::ConsumePrefix(&name, "sink:")) {
uint64_t byte_count = 0;
if (!ParseUint(&byte_count, name)) {
diff --git a/adb/daemon/shell_service.cpp b/adb/daemon/shell_service.cpp
index fbfae1e..dbca4ad 100644
--- a/adb/daemon/shell_service.cpp
+++ b/adb/daemon/shell_service.cpp
@@ -646,15 +646,21 @@
}
// After handling all of the events we've received, check to see if any fds have died.
- if (stdinout_pfd.revents & (POLLHUP | POLLRDHUP | POLLERR | POLLNVAL)) {
+ auto poll_finished = [](int events) {
+ // Don't return failure until we've read out all of the fd's incoming data.
+ return (events & POLLIN) == 0 &&
+ (events & (POLLHUP | POLLRDHUP | POLLERR | POLLNVAL)) != 0;
+ };
+
+ if (poll_finished(stdinout_pfd.revents)) {
return &stdinout_sfd_;
}
- if (stderr_pfd.revents & (POLLHUP | POLLRDHUP | POLLERR | POLLNVAL)) {
+ if (poll_finished(stderr_pfd.revents)) {
return &stderr_sfd_;
}
- if (protocol_pfd.revents & (POLLHUP | POLLRDHUP | POLLERR | POLLNVAL)) {
+ if (poll_finished(protocol_pfd.revents)) {
return &protocol_sfd_;
}
} // while (!dead_sfd)
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/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index 87937fb..a663871 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -45,19 +45,15 @@
#include <android-base/properties.h>
#include <android-base/thread_annotations.h>
-#include <adbd/usb.h>
-
#include "adb_unique_fd.h"
#include "adb_utils.h"
+#include "daemon/usb_ffs.h"
#include "sysdeps/chrono.h"
#include "transport.h"
#include "types.h"
using android::base::StringPrintf;
-// We can't find out whether we have support for AIO on ffs endpoints until we submit a read.
-static std::optional<bool> gFfsAioSupported;
-
// Not all USB controllers support operations larger than 16k, so don't go above that.
// Also, each submitted operation does an allocation in the kernel of that size, so we want to
// minimize our queue depth while still maintaining a deep enough queue to keep the USB stack fed.
@@ -235,7 +231,14 @@
offset += write_size;
}
}
- SubmitWrites();
+
+ // Wake up the worker thread to submit writes.
+ uint64_t notify = 1;
+ ssize_t rc = adb_write(worker_event_fd_.get(), ¬ify, sizeof(notify));
+ if (rc < 0) {
+ PLOG(FATAL) << "failed to notify worker eventfd to submit writes";
+ }
+
return true;
}
@@ -447,6 +450,9 @@
}
ReadEvents();
+
+ std::lock_guard<std::mutex> lock(write_mutex_);
+ SubmitWrites();
}
});
}
@@ -612,17 +618,10 @@
block->pending = true;
struct iocb* iocb = &block->control;
if (io_submit(aio_context_.get(), 1, &iocb) != 1) {
- if (errno == EINVAL && !gFfsAioSupported.has_value()) {
- HandleError("failed to submit first read, AIO on FFS not supported");
- gFfsAioSupported = false;
- return false;
- }
-
HandleError(StringPrintf("failed to submit read: %s", strerror(errno)));
return false;
}
- gFfsAioSupported = true;
return true;
}
@@ -637,8 +636,6 @@
write_requests_.erase(it);
size_t outstanding_writes = --writes_submitted_;
LOG(DEBUG) << "USB write: reaped, down to " << outstanding_writes;
-
- SubmitWrites();
}
IoWriteBlock CreateWriteBlock(std::shared_ptr<Block> payload, size_t offset, size_t len,
@@ -741,17 +738,10 @@
static constexpr int kInterruptionSignal = SIGUSR1;
};
-void usb_init_legacy();
-
static void usb_ffs_open_thread() {
adb_thread_setname("usb ffs open");
while (true) {
- if (gFfsAioSupported.has_value() && !gFfsAioSupported.value()) {
- LOG(INFO) << "failed to use nonblocking ffs, falling back to legacy";
- return usb_init_legacy();
- }
-
unique_fd control;
unique_fd bulk_out;
unique_fd bulk_in;
@@ -773,13 +763,5 @@
}
void usb_init() {
- bool use_nonblocking = android::base::GetBoolProperty(
- "persist.adb.nonblocking_ffs",
- android::base::GetBoolProperty("ro.adb.nonblocking_ffs", true));
-
- if (use_nonblocking) {
- std::thread(usb_ffs_open_thread).detach();
- } else {
- usb_init_legacy();
- }
+ std::thread(usb_ffs_open_thread).detach();
}
diff --git a/adb/daemon/usb_dummy.cpp b/adb/daemon/usb_dummy.cpp
deleted file mode 100644
index c9bf797..0000000
--- a/adb/daemon/usb_dummy.cpp
+++ /dev/null
@@ -1,42 +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 <adbd/usb.h>
-
-#include <android-base/logging.h>
-
-int usb_write(usb_handle*, const void*, int) {
- LOG(FATAL) << "unimplemented";
- return -1;
-}
-
-int usb_read(usb_handle*, void*, int) {
- LOG(FATAL) << "unimplemented";
- return -1;
-}
-
-int usb_close(usb_handle*) {
- LOG(FATAL) << "unimplemented";
- return -1;
-}
-
-void usb_reset(usb_handle*) {
- LOG(FATAL) << "unimplemented";
-}
-
-void usb_kick(usb_handle*) {
- LOG(FATAL) << "unimplemented";
-}
diff --git a/adb/daemon/usb_ffs.cpp b/adb/daemon/usb_ffs.cpp
index cb7e2fb..e538ca8 100644
--- a/adb/daemon/usb_ffs.cpp
+++ b/adb/daemon/usb_ffs.cpp
@@ -18,6 +18,8 @@
#include "sysdeps.h"
+#include "daemon/usb_ffs.h"
+
#include <linux/usb/ch9.h>
#include <linux/usb/functionfs.h>
@@ -26,7 +28,6 @@
#include <android-base/unique_fd.h>
#include "adb.h"
-#include "adbd/usb.h"
#define MAX_PACKET_SIZE_FS 64
#define MAX_PACKET_SIZE_HS 512
@@ -299,7 +300,6 @@
}
// Signal only when writing the descriptors to ffs
android::base::SetProperty("sys.usb.ffs.ready", "1");
- *out_control = std::move(control);
}
bulk_out.reset(adb_open(USB_FFS_ADB_OUT, O_RDONLY));
@@ -314,6 +314,7 @@
return false;
}
+ *out_control = std::move(control);
*out_bulk_in = std::move(bulk_in);
*out_bulk_out = std::move(bulk_out);
return true;
diff --git a/init/service_lock.cpp b/adb/daemon/usb_ffs.h
similarity index 75%
rename from init/service_lock.cpp
rename to adb/daemon/usb_ffs.h
index 404d439..a19d7cc 100644
--- a/init/service_lock.cpp
+++ b/adb/daemon/usb_ffs.h
@@ -14,12 +14,9 @@
* limitations under the License.
*/
-#include "service_lock.h"
+#pragma once
-namespace android {
-namespace init {
+#include <android-base/unique_fd.h>
-RecursiveMutex service_lock;
-
-} // namespace init
-} // namespace android
+bool open_functionfs(android::base::unique_fd* control, android::base::unique_fd* bulk_out,
+ android::base::unique_fd* bulk_in);
diff --git a/adb/fastdeploy/proto/ApkEntry.proto b/adb/fastdeploy/proto/ApkEntry.proto
index d84c5a5..ed5056e 100644
--- a/adb/fastdeploy/proto/ApkEntry.proto
+++ b/adb/fastdeploy/proto/ApkEntry.proto
@@ -5,7 +5,6 @@
option java_package = "com.android.fastdeploy";
option java_outer_classname = "ApkEntryProto";
option java_multiple_files = true;
-option optimize_for = LITE_RUNTIME;
message APKDump {
string name = 1;
diff --git a/adb/fdevent/fdevent.cpp b/adb/fdevent/fdevent.cpp
index 562f587..70cb9b3 100644
--- a/adb/fdevent/fdevent.cpp
+++ b/adb/fdevent/fdevent.cpp
@@ -27,7 +27,10 @@
#include "adb_utils.h"
#include "fdevent.h"
#include "fdevent_epoll.h"
+
+#if !defined(__linux__)
#include "fdevent_poll.h"
+#endif
using namespace std::chrono_literals;
using std::chrono::duration_cast;
@@ -63,7 +66,10 @@
int fd_num = fd.get();
- fdevent* fde = new fdevent();
+ auto [it, inserted] = this->installed_fdevents_.emplace(fd_num, fdevent{});
+ CHECK(inserted);
+
+ fdevent* fde = &it->second;
fde->id = fdevent_id_++;
fde->state = 0;
fde->fd = std::move(fd);
@@ -76,10 +82,6 @@
LOG(ERROR) << "failed to set non-blocking mode for fd " << fde->fd.get();
}
- auto [it, inserted] = this->installed_fdevents_.emplace(fd_num, fde);
- CHECK(inserted);
- UNUSED(it);
-
this->Register(fde);
return fde;
}
@@ -92,12 +94,12 @@
this->Unregister(fde);
- auto erased = this->installed_fdevents_.erase(fde->fd.get());
+ unique_fd fd = std::move(fde->fd);
+
+ auto erased = this->installed_fdevents_.erase(fd.get());
CHECK_EQ(1UL, erased);
- unique_fd result = std::move(fde->fd);
- delete fde;
- return result;
+ return fd;
}
void fdevent_context::Add(fdevent* fde, unsigned events) {
@@ -123,9 +125,9 @@
for (const auto& [fd, fde] : this->installed_fdevents_) {
UNUSED(fd);
- auto timeout_opt = fde->timeout;
+ auto timeout_opt = fde.timeout;
if (timeout_opt) {
- auto deadline = fde->last_active + *timeout_opt;
+ auto deadline = fde.last_active + *timeout_opt;
auto time_left = duration_cast<std::chrono::milliseconds>(deadline - now);
if (time_left < 0ms) {
time_left = 0ms;
@@ -194,11 +196,13 @@
#endif
}
-static auto& g_ambient_fdevent_context =
- *new std::unique_ptr<fdevent_context>(fdevent_create_context());
+static auto& g_ambient_fdevent_context() {
+ static auto context = fdevent_create_context().release();
+ return context;
+}
static fdevent_context* fdevent_get_ambient() {
- return g_ambient_fdevent_context.get();
+ return g_ambient_fdevent_context();
}
fdevent* fdevent_create(int fd, fd_func func, void* arg) {
@@ -256,5 +260,6 @@
}
void fdevent_reset() {
- g_ambient_fdevent_context = fdevent_create_context();
+ auto old = std::exchange(g_ambient_fdevent_context(), fdevent_create_context().release());
+ delete old;
}
diff --git a/adb/fdevent/fdevent.h b/adb/fdevent/fdevent.h
index 86814d7..bb3af74 100644
--- a/adb/fdevent/fdevent.h
+++ b/adb/fdevent/fdevent.h
@@ -52,6 +52,20 @@
unsigned events;
};
+struct fdevent final {
+ uint64_t id;
+
+ unique_fd fd;
+ int force_eof = 0;
+
+ uint16_t state = 0;
+ std::optional<std::chrono::milliseconds> timeout;
+ std::chrono::steady_clock::time_point last_active;
+
+ std::variant<fd_func, fd_func2> func;
+ void* arg = nullptr;
+};
+
struct fdevent_context {
public:
virtual ~fdevent_context() = default;
@@ -65,8 +79,8 @@
unique_fd Destroy(fdevent* fde);
protected:
- virtual void Register(fdevent*) {}
- virtual void Unregister(fdevent*) {}
+ virtual void Register(fdevent*) = 0;
+ virtual void Unregister(fdevent*) = 0;
public:
// Change which events should cause notifications.
@@ -113,7 +127,7 @@
std::atomic<bool> terminate_loop_ = false;
protected:
- std::unordered_map<int, fdevent*> installed_fdevents_;
+ std::unordered_map<int, fdevent> installed_fdevents_;
private:
uint64_t fdevent_id_ = 0;
@@ -121,20 +135,6 @@
std::deque<std::function<void()>> run_queue_ GUARDED_BY(run_queue_mutex_);
};
-struct fdevent {
- uint64_t id;
-
- unique_fd fd;
- int force_eof = 0;
-
- uint16_t state = 0;
- std::optional<std::chrono::milliseconds> timeout;
- std::chrono::steady_clock::time_point last_active;
-
- std::variant<fd_func, fd_func2> func;
- void* arg = nullptr;
-};
-
// Backwards compatibility shims that forward to the global fdevent_context.
fdevent* fdevent_create(int fd, fd_func func, void* arg);
fdevent* fdevent_create(int fd, fd_func2 func, void* arg);
diff --git a/adb/fdevent/fdevent_epoll.cpp b/adb/fdevent/fdevent_epoll.cpp
index e3d1674..4ef41d1 100644
--- a/adb/fdevent/fdevent_epoll.cpp
+++ b/adb/fdevent/fdevent_epoll.cpp
@@ -155,15 +155,15 @@
event_map[fde] = events;
}
- for (const auto& [fd, fde] : installed_fdevents_) {
+ for (auto& [fd, fde] : installed_fdevents_) {
unsigned events = 0;
- if (auto it = event_map.find(fde); it != event_map.end()) {
+ if (auto it = event_map.find(&fde); it != event_map.end()) {
events = it->second;
}
if (events == 0) {
- if (fde->timeout) {
- auto deadline = fde->last_active + *fde->timeout;
+ if (fde.timeout) {
+ auto deadline = fde.last_active + *fde.timeout;
if (deadline < post_poll) {
events |= FDE_TIMEOUT;
}
@@ -171,13 +171,13 @@
}
if (events != 0) {
- LOG(DEBUG) << dump_fde(fde) << " got events " << std::hex << std::showbase
+ LOG(DEBUG) << dump_fde(&fde) << " got events " << std::hex << std::showbase
<< events;
- fde_events.push_back({fde, events});
- fde->last_active = post_poll;
+ fde_events.push_back({&fde, events});
+ fde.last_active = post_poll;
}
}
- this->HandleEvents(std::move(fde_events));
+ this->HandleEvents(fde_events);
fde_events.clear();
}
diff --git a/adb/fdevent/fdevent_epoll.h b/adb/fdevent/fdevent_epoll.h
index 684fa32..6214d2e 100644
--- a/adb/fdevent/fdevent_epoll.h
+++ b/adb/fdevent/fdevent_epoll.h
@@ -47,12 +47,7 @@
protected:
virtual void Interrupt() final;
- public:
- // All operations to fdevent should happen only in the main thread.
- // That's why we don't need a lock for fdevent.
- std::unordered_map<int, fdevent*> epoll_node_map_;
- std::list<fdevent*> pending_list_;
-
+ private:
unique_fd epoll_fd_;
unique_fd interrupt_fd_;
fdevent* interrupt_fde_ = nullptr;
diff --git a/adb/fdevent/fdevent_poll.cpp b/adb/fdevent/fdevent_poll.cpp
index cc4a7a1..21c1ba0 100644
--- a/adb/fdevent/fdevent_poll.cpp
+++ b/adb/fdevent/fdevent_poll.cpp
@@ -103,24 +103,27 @@
void fdevent_context_poll::Loop() {
main_thread_id_ = android::base::GetThreadId();
+ std::vector<adb_pollfd> pollfds;
+ std::vector<fdevent_event> poll_events;
+
while (true) {
if (terminate_loop_) {
break;
}
D("--- --- waiting for events");
- std::vector<adb_pollfd> pollfds;
+ pollfds.clear();
for (const auto& [fd, fde] : this->installed_fdevents_) {
adb_pollfd pfd;
pfd.fd = fd;
pfd.events = 0;
- if (fde->state & FDE_READ) {
+ if (fde.state & FDE_READ) {
pfd.events |= POLLIN;
}
- if (fde->state & FDE_WRITE) {
+ if (fde.state & FDE_WRITE) {
pfd.events |= POLLOUT;
}
- if (fde->state & FDE_ERROR) {
+ if (fde.state & FDE_ERROR) {
pfd.events |= POLLERR;
}
#if defined(__linux__)
@@ -147,7 +150,6 @@
}
auto post_poll = std::chrono::steady_clock::now();
- std::vector<fdevent_event> poll_events;
for (const auto& pollfd : pollfds) {
unsigned events = 0;
@@ -170,7 +172,7 @@
auto it = this->installed_fdevents_.find(pollfd.fd);
CHECK(it != this->installed_fdevents_.end());
- fdevent* fde = it->second;
+ fdevent* fde = &it->second;
if (events == 0) {
if (fde->timeout) {
@@ -187,7 +189,8 @@
fde->last_active = post_poll;
}
}
- this->HandleEvents(std::move(poll_events));
+ this->HandleEvents(poll_events);
+ poll_events.clear();
}
main_thread_id_.reset();
@@ -208,3 +211,7 @@
PLOG(FATAL) << "failed to write to fdevent interrupt fd";
}
}
+
+void fdevent_context_poll::Register(fdevent*) {}
+
+void fdevent_context_poll::Unregister(fdevent*) {}
diff --git a/adb/fdevent/fdevent_poll.h b/adb/fdevent/fdevent_poll.h
index 98abab2..8803e3e 100644
--- a/adb/fdevent/fdevent_poll.h
+++ b/adb/fdevent/fdevent_poll.h
@@ -48,6 +48,9 @@
fdevent_context_poll();
virtual ~fdevent_context_poll();
+ virtual void Register(fdevent* fde) final;
+ virtual void Unregister(fdevent* fde) final;
+
virtual void Set(fdevent* fde, unsigned events) final;
virtual void Loop() final;
diff --git a/adb/file_sync_protocol.h b/adb/file_sync_protocol.h
index 508c138..5234c20 100644
--- a/adb/file_sync_protocol.h
+++ b/adb/file_sync_protocol.h
@@ -27,8 +27,10 @@
#define ID_DENT_V1 MKID('D', 'E', 'N', 'T')
#define ID_DENT_V2 MKID('D', 'N', 'T', '2')
-#define ID_SEND MKID('S', 'E', 'N', 'D')
-#define ID_RECV MKID('R', 'E', 'C', 'V')
+#define ID_SEND_V1 MKID('S', 'E', 'N', 'D')
+#define ID_SEND_V2 MKID('S', 'N', 'D', '2')
+#define ID_RECV_V1 MKID('R', 'E', 'C', 'V')
+#define ID_RECV_V2 MKID('R', 'C', 'V', '2')
#define ID_DONE MKID('D', 'O', 'N', 'E')
#define ID_DATA MKID('D', 'A', 'T', 'A')
#define ID_OKAY MKID('O', 'K', 'A', 'Y')
@@ -41,57 +43,102 @@
// Followed by 'path_length' bytes of path (not NUL-terminated).
} __attribute__((packed));
+struct __attribute__((packed)) sync_stat_v1 {
+ uint32_t id;
+ uint32_t mode;
+ uint32_t size;
+ uint32_t mtime;
+};
+
+struct __attribute__((packed)) sync_stat_v2 {
+ uint32_t id;
+ uint32_t error;
+ uint64_t dev;
+ uint64_t ino;
+ uint32_t mode;
+ uint32_t nlink;
+ uint32_t uid;
+ uint32_t gid;
+ uint64_t size;
+ int64_t atime;
+ int64_t mtime;
+ int64_t ctime;
+};
+
+struct __attribute__((packed)) sync_dent_v1 {
+ uint32_t id;
+ uint32_t mode;
+ uint32_t size;
+ uint32_t mtime;
+ uint32_t namelen;
+}; // followed by `namelen` bytes of the name.
+
+struct __attribute__((packed)) sync_dent_v2 {
+ uint32_t id;
+ uint32_t error;
+ uint64_t dev;
+ uint64_t ino;
+ uint32_t mode;
+ uint32_t nlink;
+ uint32_t uid;
+ uint32_t gid;
+ uint64_t size;
+ int64_t atime;
+ int64_t mtime;
+ int64_t ctime;
+ uint32_t namelen;
+}; // followed by `namelen` bytes of the name.
+
+enum SyncFlag : uint32_t {
+ kSyncFlagNone = 0,
+ kSyncFlagBrotli = 1,
+ kSyncFlagLZ4 = 2,
+ kSyncFlagZstd = 4,
+ kSyncFlagDryRun = 0x8000'0000U,
+};
+
+enum class CompressionType {
+ None,
+ Any,
+ Brotli,
+ LZ4,
+ Zstd,
+};
+
+// send_v1 sent the path in a buffer, followed by a comma and the mode as a string.
+// send_v2 sends just the path in the first request, and then sends another syncmsg (with the
+// same ID!) with details.
+struct __attribute__((packed)) sync_send_v2 {
+ uint32_t id;
+ uint32_t mode;
+ uint32_t flags;
+};
+
+// Likewise, recv_v1 just sent the path without any accompanying data.
+struct __attribute__((packed)) sync_recv_v2 {
+ uint32_t id;
+ uint32_t flags;
+};
+
+struct __attribute__((packed)) sync_data {
+ uint32_t id;
+ uint32_t size;
+}; // followed by `size` bytes of data.
+
+struct __attribute__((packed)) sync_status {
+ uint32_t id;
+ uint32_t msglen;
+}; // followed by `msglen` bytes of error message, if id == ID_FAIL.
+
union syncmsg {
- struct __attribute__((packed)) {
- uint32_t id;
- uint32_t mode;
- uint32_t size;
- uint32_t mtime;
- } stat_v1;
- struct __attribute__((packed)) {
- uint32_t id;
- uint32_t error;
- uint64_t dev;
- uint64_t ino;
- uint32_t mode;
- uint32_t nlink;
- uint32_t uid;
- uint32_t gid;
- uint64_t size;
- int64_t atime;
- int64_t mtime;
- int64_t ctime;
- } stat_v2;
- struct __attribute__((packed)) {
- uint32_t id;
- uint32_t mode;
- uint32_t size;
- uint32_t mtime;
- uint32_t namelen;
- } dent_v1; // followed by `namelen` bytes of the name.
- struct __attribute__((packed)) {
- uint32_t id;
- uint32_t error;
- uint64_t dev;
- uint64_t ino;
- uint32_t mode;
- uint32_t nlink;
- uint32_t uid;
- uint32_t gid;
- uint64_t size;
- int64_t atime;
- int64_t mtime;
- int64_t ctime;
- uint32_t namelen;
- } dent_v2; // followed by `namelen` bytes of the name.
- struct __attribute__((packed)) {
- uint32_t id;
- uint32_t size;
- } data; // followed by `size` bytes of data.
- struct __attribute__((packed)) {
- uint32_t id;
- uint32_t msglen;
- } status; // followed by `msglen` bytes of error message, if id == ID_FAIL.
+ sync_stat_v1 stat_v1;
+ sync_stat_v2 stat_v2;
+ sync_dent_v1 dent_v1;
+ sync_dent_v2 dent_v2;
+ sync_data data;
+ sync_status status;
+ sync_send_v2 send_v2_setup;
+ sync_recv_v2 recv_v2_setup;
};
#define SYNC_DATA_MAX (64 * 1024)
diff --git a/adb/libs/adbconnection/Android.bp b/adb/libs/adbconnection/Android.bp
index f6b0a42..f7d2dc1 100644
--- a/adb/libs/adbconnection/Android.bp
+++ b/adb/libs/adbconnection/Android.bp
@@ -18,6 +18,11 @@
use_version_lib: false,
recovery_available: true,
+ apex_available: [
+ "com.android.adbd",
+ // TODO(b/151398197) remove the below
+ "//apex_available:platform",
+ ],
compile_multilib: "both",
}
@@ -48,6 +53,7 @@
linux: {
version_script: "libadbconnection_client.map.txt",
},
+ darwin: { enabled: false },
},
stubs: {
symbol_file: "libadbconnection_client.map.txt",
diff --git a/adb/libs/adbconnection/adbconnection_client.cpp b/adb/libs/adbconnection/adbconnection_client.cpp
index ee48abb..7e16148 100644
--- a/adb/libs/adbconnection/adbconnection_client.cpp
+++ b/adb/libs/adbconnection/adbconnection_client.cpp
@@ -29,6 +29,8 @@
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
+#include "adbconnection/process_info.h"
+
using android::base::unique_fd;
static constexpr char kJdwpControlName[] = "\0jdwp-control";
@@ -60,6 +62,8 @@
std::optional<uint64_t> pid;
std::optional<bool> debuggable;
+ std::optional<bool> profileable;
+ std::optional<std::string> architecture;
for (size_t i = 0; i < info_count; ++i) {
auto info = info_elems[i];
@@ -77,7 +81,23 @@
LOG(ERROR) << "multiple debuggable entries in AdbConnectionClientInfo, ignoring";
continue;
}
- debuggable = info->data.pid;
+ debuggable = info->data.debuggable;
+ break;
+
+ case AdbConnectionClientInfoType::profileable:
+ if (profileable) {
+ LOG(ERROR) << "multiple profileable entries in AdbConnectionClientInfo, ignoring";
+ continue;
+ }
+ profileable = info->data.profileable;
+ break;
+
+ case AdbConnectionClientInfoType::architecture:
+ if (architecture) {
+ LOG(ERROR) << "multiple architecture entries in AdbConnectionClientInfo, ignoring";
+ continue;
+ }
+ architecture = std::string(info->data.architecture.name, info->data.architecture.size);
break;
}
}
@@ -92,6 +112,16 @@
return nullptr;
}
+ if (!profileable) {
+ LOG(ERROR) << "AdbConnectionClientInfo missing required field profileable";
+ return nullptr;
+ }
+
+ if (!architecture) {
+ LOG(ERROR) << "AdbConnectionClientInfo missing required field architecture";
+ return nullptr;
+ }
+
ctx->control_socket_.reset(socket(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0));
if (ctx->control_socket_ < 0) {
PLOG(ERROR) << "failed to create Unix domain socket";
@@ -110,7 +140,13 @@
int rc = connect(ctx->control_socket_.get(), reinterpret_cast<sockaddr*>(&addr), addr_len);
if (rc != 0) {
- PLOG(ERROR) << "failed to connect to jdwp control socket";
+ if (errno == ECONNREFUSED) {
+ // On userdebug devices, every Java process is debuggable, so if adbd is explicitly turned
+ // off, this would spew enormous amounts of red-herring errors.
+ LOG(DEBUG) << "failed to connect to jdwp control socket, adbd not running?";
+ } else {
+ PLOG(ERROR) << "failed to connect to jdwp control socket";
+ }
return nullptr;
}
@@ -120,10 +156,10 @@
return nullptr;
}
- uint32_t pid_u32 = static_cast<uint32_t>(*pid);
- rc = TEMP_FAILURE_RETRY(write(ctx->control_socket_.get(), &pid_u32, sizeof(pid_u32)));
- if (rc != sizeof(pid_u32)) {
- PLOG(ERROR) << "failed to send JDWP process pid to adbd";
+ ProcessInfo process(*pid, *debuggable, *profileable, *architecture);
+ rc = TEMP_FAILURE_RETRY(write(ctx->control_socket_.get(), &process, sizeof(process)));
+ if (rc != sizeof(process)) {
+ PLOG(ERROR) << "failed to send JDWP process info to adbd";
}
return ctx.release();
diff --git a/adb/libs/adbconnection/adbconnection_server.cpp b/adb/libs/adbconnection/adbconnection_server.cpp
index 939da2f..aac9615 100644
--- a/adb/libs/adbconnection/adbconnection_server.cpp
+++ b/adb/libs/adbconnection/adbconnection_server.cpp
@@ -28,6 +28,8 @@
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
+#include "adbconnection/process_info.h"
+
using android::base::unique_fd;
#define JDWP_CONTROL_NAME "\0jdwp-control"
@@ -36,7 +38,7 @@
static_assert(JDWP_CONTROL_NAME_LEN <= sizeof(reinterpret_cast<sockaddr_un*>(0)->sun_path));
// Listen for incoming jdwp clients forever.
-void adbconnection_listen(void (*callback)(int fd, pid_t pid)) {
+void adbconnection_listen(void (*callback)(int fd, ProcessInfo process)) {
sockaddr_un addr = {};
socklen_t addrlen = JDWP_CONTROL_NAME_LEN + sizeof(addr.sun_family);
@@ -106,16 +108,13 @@
<< ") in pending connections";
}
- // Massively oversized buffer: we're expecting an int32_t from the other end.
- char buf[32];
- int rc = TEMP_FAILURE_RETRY(recv(it->get(), buf, sizeof(buf), MSG_DONTWAIT));
- if (rc != 4) {
+ ProcessInfo process;
+ int rc = TEMP_FAILURE_RETRY(recv(it->get(), &process, sizeof(process), MSG_DONTWAIT));
+ if (rc != sizeof(process)) {
LOG(ERROR) << "received data of incorrect size from JDWP client: read " << rc
- << ", expected 4";
+ << ", expected " << sizeof(process);
} else {
- int32_t pid;
- memcpy(&pid, buf, sizeof(pid));
- callback(it->release(), static_cast<pid_t>(pid));
+ callback(it->release(), process);
}
if (epoll_ctl(epfd.get(), EPOLL_CTL_DEL, event.data.fd, nullptr) != 0) {
diff --git a/adb/libs/adbconnection/include/adbconnection/client.h b/adb/libs/adbconnection/include/adbconnection/client.h
index 692fea0..a74cd36 100644
--- a/adb/libs/adbconnection/include/adbconnection/client.h
+++ b/adb/libs/adbconnection/include/adbconnection/client.h
@@ -28,6 +28,8 @@
enum AdbConnectionClientInfoType {
pid,
debuggable,
+ profileable,
+ architecture,
};
struct AdbConnectionClientInfo {
@@ -35,11 +37,17 @@
union {
uint64_t pid;
bool debuggable;
+ bool profileable;
+ struct {
+ const char* name;
+ size_t size;
+ } architecture;
} data;
};
// Construct a context and connect to adbd.
// Returns null if we fail to connect to adbd.
+// Note this is an apex interface as it's loaded by ART.
AdbConnectionClientContext* adbconnection_client_new(
const AdbConnectionClientInfo* const* info_elems, size_t info_count);
diff --git a/adb/libs/adbconnection/include/adbconnection/process_info.h b/adb/libs/adbconnection/include/adbconnection/process_info.h
new file mode 100644
index 0000000..d226699
--- /dev/null
+++ b/adb/libs/adbconnection/include/adbconnection/process_info.h
@@ -0,0 +1,39 @@
+/*
+ * 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 <stdint.h>
+#include <string.h>
+#include <string>
+
+struct ProcessInfo {
+ static constexpr size_t kMaxArchNameLength = 16;
+
+ uint64_t pid;
+ bool debuggable;
+ bool profileable;
+ int32_t arch_name_length; // length of architecture name in bytes
+ char arch_name[kMaxArchNameLength]; // ISA name, e.g., "arm64"
+
+ ProcessInfo() : pid(0), debuggable(false), profileable(false), arch_name_length(0) {}
+
+ ProcessInfo(uint64_t pid, bool dbg, bool prof, const std::string& arch)
+ : pid(pid), debuggable(dbg), profileable(prof) {
+ arch_name_length = std::min(arch.size(), kMaxArchNameLength);
+ memcpy(arch_name, arch.data(), arch_name_length);
+ }
+};
diff --git a/adb/libs/adbconnection/include/adbconnection/server.h b/adb/libs/adbconnection/include/adbconnection/server.h
index 57ca6cd..b1059ba 100644
--- a/adb/libs/adbconnection/include/adbconnection/server.h
+++ b/adb/libs/adbconnection/include/adbconnection/server.h
@@ -20,7 +20,7 @@
#include <android-base/unique_fd.h>
-extern "C" {
+#include "adbconnection/process_info.h"
-void adbconnection_listen(void (*callback)(int fd, pid_t pid));
-}
+// Note this is NOT an apex interface as it's linked only into adbd.
+void adbconnection_listen(void (*callback)(int fd, ProcessInfo process));
diff --git a/adb/mdns_test.cpp b/adb/mdns_test.cpp
new file mode 100644
index 0000000..1f662c1
--- /dev/null
+++ b/adb/mdns_test.cpp
@@ -0,0 +1,107 @@
+/*
+ * 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 <gtest/gtest.h>
+
+#include "adb_mdns.h"
+
+static bool isValidMdnsServiceName(std::string_view name) {
+ // The rules for Service Names [RFC6335] state that they may be no more
+ // than fifteen characters long (not counting the mandatory underscore),
+ // consisting of only letters, digits, and hyphens, must begin and end
+ // with a letter or digit, must not contain consecutive hyphens, and
+ // must contain at least one letter.
+
+ // No more than 15 characters long
+ if (name.empty() || name.size() > 15) {
+ return false;
+ }
+
+ bool hasAtLeastOneLetter = false;
+ bool sawHyphen = false;
+ for (size_t i = 0; i < name.size(); ++i) {
+ // Must contain at least one letter
+ // Only contains letters, digits and hyphens
+ if (name[i] == '-') {
+ // Cannot be at beginning or end
+ if (i == 0 || i == name.size() - 1) {
+ return false;
+ }
+ if (sawHyphen) {
+ // Consecutive hyphen found
+ return false;
+ }
+ sawHyphen = true;
+ continue;
+ }
+
+ sawHyphen = false;
+ if ((name[i] >= 'a' && name[i] <= 'z') || (name[i] >= 'A' && name[i] <= 'Z')) {
+ hasAtLeastOneLetter = true;
+ continue;
+ }
+
+ if (name[i] >= '0' && name[i] <= '9') {
+ continue;
+ }
+
+ // Invalid character
+ return false;
+ }
+
+ return hasAtLeastOneLetter;
+}
+
+TEST(mdns, test_isValidMdnsServiceName) {
+ // Longer than 15 characters
+ EXPECT_FALSE(isValidMdnsServiceName("abcd1234abcd1234"));
+
+ // Contains invalid characters
+ EXPECT_FALSE(isValidMdnsServiceName("a*a"));
+ EXPECT_FALSE(isValidMdnsServiceName("a_a"));
+ EXPECT_FALSE(isValidMdnsServiceName("_a"));
+
+ // Does not begin or end with letter or digit
+ EXPECT_FALSE(isValidMdnsServiceName(""));
+ EXPECT_FALSE(isValidMdnsServiceName("-"));
+ EXPECT_FALSE(isValidMdnsServiceName("-a"));
+ EXPECT_FALSE(isValidMdnsServiceName("-1"));
+ EXPECT_FALSE(isValidMdnsServiceName("a-"));
+ EXPECT_FALSE(isValidMdnsServiceName("1-"));
+
+ // Contains consecutive hyphens
+ EXPECT_FALSE(isValidMdnsServiceName("a--a"));
+
+ // Does not contain at least one letter
+ EXPECT_FALSE(isValidMdnsServiceName("1"));
+ EXPECT_FALSE(isValidMdnsServiceName("12"));
+ EXPECT_FALSE(isValidMdnsServiceName("1-2"));
+
+ // Some valid names
+ EXPECT_TRUE(isValidMdnsServiceName("a"));
+ EXPECT_TRUE(isValidMdnsServiceName("a1"));
+ EXPECT_TRUE(isValidMdnsServiceName("1A"));
+ EXPECT_TRUE(isValidMdnsServiceName("aZ"));
+ EXPECT_TRUE(isValidMdnsServiceName("a-Z"));
+ EXPECT_TRUE(isValidMdnsServiceName("a-b-Z"));
+ EXPECT_TRUE(isValidMdnsServiceName("abc-def-123-456"));
+}
+
+TEST(mdns, ServiceName_RFC6335) {
+ EXPECT_TRUE(isValidMdnsServiceName(ADB_MDNS_SERVICE_TYPE));
+ EXPECT_TRUE(isValidMdnsServiceName(ADB_MDNS_TLS_PAIRING_TYPE));
+ EXPECT_TRUE(isValidMdnsServiceName(ADB_MDNS_TLS_CONNECT_TYPE));
+}
diff --git a/adb/pairing_auth/aes_128_gcm.cpp b/adb/pairing_auth/aes_128_gcm.cpp
index 2978834..51520d8 100644
--- a/adb/pairing_auth/aes_128_gcm.cpp
+++ b/adb/pairing_auth/aes_128_gcm.cpp
@@ -19,7 +19,6 @@
#include <android-base/endian.h>
#include <android-base/logging.h>
-#include <openssl/crypto.h>
#include <openssl/evp.h>
#include <openssl/hkdf.h>
#include <openssl/rand.h>
@@ -28,155 +27,64 @@
namespace pairing {
namespace {
-static const size_t kHkdfKeyLength = 256;
-
-struct Header {
- uint32_t payload;
- uint8_t iv[AES_128_GCM_IV_SIZE];
- uint8_t tag[AES_128_GCM_TAG_SIZE];
-} __attribute__((packed));
+// Size of AES-128-GCM key, in bytes
+static constexpr size_t kHkdfKeyLength = 16;
} // namespace
-// static
-const EVP_CIPHER* Aes128Gcm::cipher_ = EVP_aes_128_gcm();
-
Aes128Gcm::Aes128Gcm(const uint8_t* key_material, size_t key_material_len) {
CHECK(key_material);
CHECK_NE(key_material_len, 0ul);
- context_.reset(EVP_CIPHER_CTX_new());
- CHECK(context_.get());
- // Start with a random number for our counter
- CHECK_EQ(RAND_bytes(counter_.data(), counter_.size()), 1);
-
- uint8_t key[kHkdfKeyLength] = {};
- uint8_t salt[64] = "this is the salt";
- uint8_t info[64] = "this is the info";
- CHECK_EQ(HKDF(key, sizeof(key), EVP_sha256(), key_material, key_material_len, salt,
- sizeof(salt), info, sizeof(info)),
+ uint8_t key[kHkdfKeyLength];
+ uint8_t info[] = "adb pairing_auth aes-128-gcm key";
+ CHECK_EQ(HKDF(key, sizeof(key), EVP_sha256(), key_material, key_material_len, nullptr, 0, info,
+ sizeof(info) - 1),
1);
- CHECK_EQ(AES_set_encrypt_key(key, sizeof(key), &aes_key_), 0);
+ CHECK(EVP_AEAD_CTX_init(context_.get(), EVP_aead_aes_128_gcm(), key, sizeof(key),
+ EVP_AEAD_DEFAULT_TAG_LENGTH, nullptr));
}
-int Aes128Gcm::Encrypt(const uint8_t* in, size_t in_len, uint8_t* out, size_t out_len) {
- if (out_len < EncryptedSize(in_len)) {
- LOG(ERROR) << "out buffer size (sz=" << out_len
- << ") not big enough (sz=" << EncryptedSize(in_len) << ")";
- return -1;
- }
- auto& header = *reinterpret_cast<Header*>(out);
- // Place the IV in the header
- memcpy(header.iv, counter_.data(), counter_.size());
- int status = EVP_EncryptInit_ex(context_.get(), cipher_, nullptr,
- reinterpret_cast<const uint8_t*>(&aes_key_), counter_.data());
- counter_.Increase();
- if (status != 1) {
- return -1;
+std::optional<size_t> Aes128Gcm::Encrypt(const uint8_t* in, size_t in_len, uint8_t* out,
+ size_t out_len) {
+ std::vector<uint8_t> nonce(EVP_AEAD_nonce_length(EVP_AEAD_CTX_aead(context_.get())), 0);
+ memcpy(nonce.data(), &enc_sequence_, sizeof(enc_sequence_));
+ size_t written_sz;
+ if (!EVP_AEAD_CTX_seal(context_.get(), out, &written_sz, out_len, nonce.data(), nonce.size(),
+ in, in_len, nullptr, 0)) {
+ LOG(ERROR) << "Failed to encrypt (in_len=" << in_len << ", out_len=" << out_len
+ << ", out_len_needed=" << EncryptedSize(in_len) << ")";
+ return std::nullopt;
}
- int cipherLen = 0;
- out += sizeof(header);
- status = EVP_EncryptUpdate(context_.get(), out, &cipherLen, in, in_len);
- if (status != 1 || cipherLen < 0) {
- return -1;
- }
-
- // Padding is enabled by default, so EVP_EncryptFinal_ex will pad any
- // remaining partial data up to the block size.
- int padding = 0;
- status = EVP_EncryptFinal_ex(context_.get(), out + cipherLen, &padding);
- if (status != 1 || padding < 0) {
- return -1;
- }
-
- // Place the tag in the header
- status = EVP_CIPHER_CTX_ctrl(context_.get(), EVP_CTRL_GCM_GET_TAG, sizeof(header.tag),
- header.tag);
- if (status != 1) {
- return -1;
- }
- // Place the payload size in the header
- uint32_t totalLen = sizeof(header) + cipherLen + padding;
- header.payload = htonl(static_cast<uint32_t>(cipherLen) + static_cast<uint32_t>(padding));
- return totalLen;
+ ++enc_sequence_;
+ return written_sz;
}
-int Aes128Gcm::Decrypt(const uint8_t* in, size_t in_len, uint8_t* out, size_t out_len) {
- if (in_len < sizeof(Header)) {
- return 0;
- }
- if (out_len < DecryptedSize(in, in_len)) {
- return 0;
- }
- const auto& header = *reinterpret_cast<const Header*>(in);
- uint32_t payload = ntohl(header.payload);
- uint32_t expected_inlen = sizeof(Header) + payload;
- if (in_len < expected_inlen) {
- // Not enough data available
- return 0;
- }
- // Initialized with expected IV from header
- int status = EVP_DecryptInit_ex(context_.get(), cipher_, nullptr,
- reinterpret_cast<const uint8_t*>(&aes_key_), header.iv);
- if (status != 1) {
- return -1;
+std::optional<size_t> Aes128Gcm::Decrypt(const uint8_t* in, size_t in_len, uint8_t* out,
+ size_t out_len) {
+ std::vector<uint8_t> nonce(EVP_AEAD_nonce_length(EVP_AEAD_CTX_aead(context_.get())), 0);
+ memcpy(nonce.data(), &dec_sequence_, sizeof(dec_sequence_));
+ size_t written_sz;
+ if (!EVP_AEAD_CTX_open(context_.get(), out, &written_sz, out_len, nonce.data(), nonce.size(),
+ in, in_len, nullptr, 0)) {
+ LOG(ERROR) << "Failed to decrypt (in_len=" << in_len << ", out_len=" << out_len
+ << ", out_len_needed=" << DecryptedSize(in_len) << ")";
+ return std::nullopt;
}
- int decrypted_len = 0;
- status = EVP_DecryptUpdate(context_.get(), out, &decrypted_len, in + sizeof(header), payload);
- if (status != 1 || decrypted_len < 0) {
- return -1;
- }
-
- // Set expected tag from header
- status = EVP_CIPHER_CTX_ctrl(context_.get(), EVP_CTRL_GCM_SET_TAG, sizeof(header.tag),
- const_cast<uint8_t*>(header.tag));
- if (status != 1) {
- return -1;
- }
-
- // This is the padding. It can be ignored.
- int len = 0;
- status = EVP_DecryptFinal_ex(context_.get(), out + decrypted_len, &len);
- if (status != 1) {
- LOG(ERROR) << "EVP_DecryptFinal_ex failed. Tag mismatch";
- return -1;
- }
-
- // Return the length without the padding.
- return decrypted_len;
+ ++dec_sequence_;
+ return written_sz;
}
size_t Aes128Gcm::EncryptedSize(size_t size) {
- // We need to account for block alignment of the encrypted data.
- // According to openssl.org/docs/man1.0.2/man3/EVP_EncryptUpdate.html,
- // "The amount of data written depends on the block alignment of the
- // encrypted data ..."
- // ".. the amount of data written may be anything from zero bytes to
- // (inl + cipher_block_size - 1) ..."
- const size_t cipher_block_size = EVP_CIPHER_block_size(cipher_);
- size_t padding = cipher_block_size - (size % cipher_block_size);
- if (padding != cipher_block_size) {
- size += padding;
- }
- return size + sizeof(Header);
+ // https://commondatastorage.googleapis.com/chromium-boringssl-docs/aead.h.html#EVP_AEAD_CTX_seal
+ return size + EVP_AEAD_max_overhead(EVP_AEAD_CTX_aead(context_.get()));
}
-size_t Aes128Gcm::DecryptedSize(const uint8_t* encrypted_data, size_t encrypted_size) {
- if (encrypted_size < sizeof(Header)) {
- // Not enough data yet
- return 0;
- }
- auto header = reinterpret_cast<const Header*>(encrypted_data);
- uint32_t payload = ntohl(header->payload);
- size_t total_size = payload + sizeof(Header);
- if (encrypted_size < total_size) {
- // There's enough data for the header but not enough data for the
- // payload. Indicate that there's not enough data for now.
- return 0;
- }
- return payload;
+size_t Aes128Gcm::DecryptedSize(size_t size) {
+ // https://commondatastorage.googleapis.com/chromium-boringssl-docs/aead.h.html#EVP_AEAD_CTX_open
+ return size;
}
} // namespace pairing
diff --git a/adb/pairing_auth/include/adb/pairing/aes_128_gcm.h b/adb/pairing_auth/include/adb/pairing/aes_128_gcm.h
index 490dd12..6be5856 100644
--- a/adb/pairing_auth/include/adb/pairing/aes_128_gcm.h
+++ b/adb/pairing_auth/include/adb/pairing/aes_128_gcm.h
@@ -16,17 +16,12 @@
#pragma once
-#include <openssl/aes.h>
-#include <openssl/cipher.h>
-
#include <stdint.h>
-#include "adb/pairing/counter.h"
+#include <optional>
+#include <vector>
-// This is the default size of the initialization vector (iv) for AES-128-GCM
-#define AES_128_GCM_IV_SIZE 12
-// This is the full tag size for AES-128-GCM
-#define AES_128_GCM_TAG_SIZE 16
+#include <openssl/aead.h>
namespace adb {
namespace pairing {
@@ -42,7 +37,7 @@
// suitable for decryption with this class.
// The method returns the number of bytes placed in |out| on success and a
// negative value if an error occurs.
- int Encrypt(const uint8_t* in, size_t in_len, uint8_t* out, size_t out_len);
+ std::optional<size_t> Encrypt(const uint8_t* in, size_t in_len, uint8_t* out, size_t out_len);
// Decrypt a block of data in |in| of length |in_len|, this consumes all data
// in |in_len| bytes of data. The decrypted output is placed in the |out|
// buffer of length |out_len|. On successful decryption the number of bytes in
@@ -50,22 +45,18 @@
// The method returns the number of bytes consumed from the |in| buffer. If
// there is not enough data available in |in| the method returns zero. If
// an error occurs the method returns a negative value.
- int Decrypt(const uint8_t* in, size_t in_len, uint8_t* out, size_t out_len);
+ std::optional<size_t> Decrypt(const uint8_t* in, size_t in_len, uint8_t* out, size_t out_len);
// Return a safe amount of buffer storage needed to encrypt |size| bytes.
size_t EncryptedSize(size_t size);
- // Return a safe amount of buffer storage needed to decrypt the encrypted
- // data in |encrypted_data| which is of length |encrypted_size|. Returns 0 if
- // there is not enough data available to determine the required size.
- size_t DecryptedSize(const uint8_t* encrypted_data, size_t encrypted_size);
-
- static const EVP_CIPHER* cipher_;
+ // Return a safe amount of buffer storage needed to decrypt |size| bytes.
+ size_t DecryptedSize(size_t size);
private:
- bssl::UniquePtr<EVP_CIPHER_CTX> context_;
- AES_KEY aes_key_;
- // We're going to use this counter for our iv so that it never repeats
- Counter<AES_128_GCM_IV_SIZE> counter_;
+ bssl::ScopedEVP_AEAD_CTX context_;
+ // Sequence numbers to use as nonces in the encryption scheme
+ uint64_t dec_sequence_ = 0;
+ uint64_t enc_sequence_ = 0;
};
} // namespace pairing
diff --git a/adb/pairing_auth/include/adb/pairing/counter.h b/adb/pairing_auth/include/adb/pairing/counter.h
deleted file mode 100644
index 263ceb7..0000000
--- a/adb/pairing_auth/include/adb/pairing/counter.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <stddef.h>
-#include <stdint.h>
-
-namespace adb {
-namespace pairing {
-
-template <size_t N>
-class Counter {
- public:
- void Increase() {
- for (size_t i = sizeof(counter_) - 1; i < sizeof(counter_); --i) {
- if (++counter_[i] != 0) {
- break;
- }
- }
- }
-
- uint8_t* data() { return counter_; }
- const uint8_t* data() const { return counter_; }
-
- constexpr size_t size() const { return sizeof(counter_); }
-
- uint8_t& operator[](size_t index) { return counter_[index]; }
- const uint8_t& operator[](size_t index) const { return counter_[index]; }
-
- private:
- uint8_t counter_[N];
-};
-
-} // namespace pairing
-} // namespace adb
diff --git a/adb/pairing_auth/pairing_auth.cpp b/adb/pairing_auth/pairing_auth.cpp
index 96bc110..0ac04e6 100644
--- a/adb/pairing_auth/pairing_auth.cpp
+++ b/adb/pairing_auth/pairing_auth.cpp
@@ -75,8 +75,8 @@
// Returns a safe buffer size for encrypting a buffer of size |len|.
size_t SafeEncryptedSize(size_t len);
- // Returns a safe buffer size for decrypting a buffer |buf|.
- size_t SafeDecryptedSize(const Data& buf);
+ // Returns a safe buffer size for decrypting a buffer of size |len|.
+ size_t SafeDecryptedSize(size_t len);
private:
Data our_msg_;
@@ -167,12 +167,12 @@
// Determine the size for the encrypted data based on the raw data.
Data encrypted(cipher_->EncryptedSize(data.size()));
- int bytes = cipher_->Encrypt(data.data(), data.size(), encrypted.data(), encrypted.size());
- if (bytes < 0) {
+ auto out_size = cipher_->Encrypt(data.data(), data.size(), encrypted.data(), encrypted.size());
+ if (!out_size.has_value() || *out_size == 0) {
LOG(ERROR) << "Unable to encrypt data";
return Data();
}
- encrypted.resize(bytes);
+ encrypted.resize(*out_size);
return encrypted;
}
@@ -182,14 +182,14 @@
CHECK(!data.empty());
// Determine the size for the decrypted data based on the raw data.
- Data decrypted(cipher_->DecryptedSize(data.data(), data.size()));
+ Data decrypted(cipher_->DecryptedSize(data.size()));
size_t decrypted_size = decrypted.size();
- int bytes = cipher_->Decrypt(data.data(), data.size(), decrypted.data(), decrypted_size);
- if (bytes <= 0) {
+ auto out_size = cipher_->Decrypt(data.data(), data.size(), decrypted.data(), decrypted_size);
+ if (!out_size.has_value() || *out_size == 0) {
LOG(ERROR) << "Unable to decrypt data";
return Data();
}
- decrypted.resize(bytes);
+ decrypted.resize(*out_size);
return decrypted;
}
@@ -199,9 +199,9 @@
return cipher_->EncryptedSize(len);
}
-size_t PairingAuthCtx::SafeDecryptedSize(const PairingAuthCtx::Data& buf) {
+size_t PairingAuthCtx::SafeDecryptedSize(size_t len) {
CHECK(cipher_);
- return cipher_->DecryptedSize(buf.data(), buf.size());
+ return cipher_->DecryptedSize(len);
}
PairingAuthCtx* pairing_auth_server_new(const uint8_t* pswd, size_t len) {
@@ -271,8 +271,8 @@
CHECK(ctx);
CHECK(buf);
CHECK_GT(len, 0U);
- std::vector<uint8_t> p(buf, buf + len);
- return ctx->SafeDecryptedSize(p);
+ // We no longer need buf for EVP_AEAD
+ return ctx->SafeDecryptedSize(len);
}
bool pairing_auth_decrypt(PairingAuthCtx* ctx, const uint8_t* inbuf, size_t inlen, uint8_t* outbuf,
diff --git a/adb/pairing_auth/tests/Android.bp b/adb/pairing_auth/tests/Android.bp
index 292fff5..213123d 100644
--- a/adb/pairing_auth/tests/Android.bp
+++ b/adb/pairing_auth/tests/Android.bp
@@ -18,7 +18,6 @@
name: "adb_pairing_auth_test",
srcs: [
"aes_128_gcm_test.cpp",
- "counter_test.cpp",
"pairing_auth_test.cpp",
],
diff --git a/adb/pairing_auth/tests/aes_128_gcm_test.cpp b/adb/pairing_auth/tests/aes_128_gcm_test.cpp
index e1a20e8..55689d6 100644
--- a/adb/pairing_auth/tests/aes_128_gcm_test.cpp
+++ b/adb/pairing_auth/tests/aes_128_gcm_test.cpp
@@ -39,7 +39,7 @@
const uint8_t msg[] = "alice and bob, sitting in a binary tree";
uint8_t material[256];
uint8_t encrypted[1024];
- uint8_t out_buf[1024];
+ uint8_t out_buf[1024] = {};
RAND_bytes(material, sizeof(material));
Aes128Gcm alice(material, sizeof(material));
@@ -47,82 +47,16 @@
;
ASSERT_GE(alice.EncryptedSize(sizeof(msg)), sizeof(msg));
- int encrypted_size = alice.Encrypt(msg, sizeof(msg), encrypted, sizeof(encrypted));
- ASSERT_GT(encrypted_size, 0);
+ auto encrypted_size = alice.Encrypt(msg, sizeof(msg), encrypted, sizeof(encrypted));
+ ASSERT_TRUE(encrypted_size.has_value());
+ ASSERT_GT(*encrypted_size, 0);
size_t out_size = sizeof(out_buf);
- ASSERT_GE(bob.DecryptedSize(encrypted, sizeof(encrypted)), sizeof(msg));
- int decrypted_size = bob.Decrypt(encrypted, sizeof(encrypted), out_buf, out_size);
- ASSERT_EQ(sizeof(msg), decrypted_size);
- memset(out_buf + decrypted_size, 0, sizeof(out_buf) - decrypted_size);
+ ASSERT_GE(bob.DecryptedSize(*encrypted_size), sizeof(msg));
+ auto decrypted_size = bob.Decrypt(encrypted, *encrypted_size, out_buf, out_size);
+ ASSERT_TRUE(decrypted_size.has_value());
+ ASSERT_EQ(sizeof(msg), *decrypted_size);
ASSERT_STREQ(reinterpret_cast<const char*>(msg), reinterpret_cast<const char*>(out_buf));
}
-TEST(Aes128GcmTest, padding) {
- // Test with block-align data as well as unaligned data.
- const size_t cipher_block_size = EVP_CIPHER_block_size(Aes128Gcm::cipher_);
- uint8_t material[256];
- RAND_bytes(material, sizeof(material));
- Aes128Gcm alice(material, sizeof(material));
- Aes128Gcm bob(material, sizeof(material));
- ;
- std::vector<uint8_t> msg;
- std::vector<uint8_t> encrypted;
- std::vector<uint8_t> decrypted;
-
- // Test with aligned data
- {
- msg.resize(cipher_block_size);
- RAND_bytes(msg.data(), msg.size());
-
- // encrypt
- size_t safe_encrypted_sz = alice.EncryptedSize(msg.size());
- ASSERT_GE(safe_encrypted_sz, msg.size());
- encrypted.resize(safe_encrypted_sz);
- int encrypted_size =
- alice.Encrypt(msg.data(), msg.size(), encrypted.data(), encrypted.size());
- ASSERT_GT(encrypted_size, 0);
- ASSERT_LE(encrypted_size, safe_encrypted_sz);
- encrypted.resize(encrypted_size);
-
- // decrypt
- size_t safe_decrypted_size = bob.DecryptedSize(encrypted.data(), encrypted.size());
- ASSERT_GE(safe_decrypted_size, msg.size());
- decrypted.resize(safe_decrypted_size);
- int decrypted_size =
- bob.Decrypt(encrypted.data(), encrypted.size(), decrypted.data(), decrypted.size());
- ASSERT_GT(decrypted_size, 0);
- ASSERT_LE(decrypted_size, safe_decrypted_size);
- ASSERT_EQ(msg.size(), decrypted_size);
- ASSERT_EQ(memcmp(msg.data(), decrypted.data(), decrypted.size()), 0);
- }
-
- // Test with unaligned data
- {
- msg.resize(cipher_block_size + 1);
- RAND_bytes(msg.data(), msg.size());
-
- // encrypt
- size_t safe_encrypted_sz = alice.EncryptedSize(msg.size());
- ASSERT_GE(safe_encrypted_sz, msg.size());
- encrypted.resize(safe_encrypted_sz);
- int encrypted_size =
- alice.Encrypt(msg.data(), msg.size(), encrypted.data(), encrypted.size());
- ASSERT_GT(encrypted_size, 0);
- ASSERT_LE(encrypted_size, safe_encrypted_sz);
- encrypted.resize(encrypted_size);
-
- // decrypt
- size_t safe_decrypted_size = bob.DecryptedSize(encrypted.data(), encrypted.size());
- ASSERT_GE(safe_decrypted_size, msg.size());
- decrypted.resize(safe_decrypted_size);
- int decrypted_size =
- bob.Decrypt(encrypted.data(), encrypted.size(), decrypted.data(), decrypted.size());
- ASSERT_GT(decrypted_size, 0);
- ASSERT_LE(decrypted_size, safe_decrypted_size);
- ASSERT_EQ(msg.size(), decrypted_size);
- ASSERT_EQ(memcmp(msg.data(), decrypted.data(), decrypted.size()), 0);
- }
-}
-
} // namespace pairing
} // namespace adb
diff --git a/adb/pairing_auth/tests/counter_test.cpp b/adb/pairing_auth/tests/counter_test.cpp
deleted file mode 100644
index b338551..0000000
--- a/adb/pairing_auth/tests/counter_test.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-
-#include <adb/pairing/counter.h>
-
-namespace adb {
-namespace pairing {
-
-static constexpr size_t kTestCounterSize = 13;
-static const uint8_t kZeroes[64] = {0};
-
-TEST(AdbCounterTest, size_match) {
- Counter<kTestCounterSize> counter;
- ASSERT_EQ(kTestCounterSize, counter.size());
-}
-
-TEST(AdbCounterTest, Increase) {
- Counter<kTestCounterSize> counter;
- memset(counter.data(), 0, counter.size());
- counter.Increase();
- EXPECT_EQ(1, counter[counter.size() - 1]);
- EXPECT_EQ(0, memcmp(counter.data(), kZeroes, counter.size() - 1));
-}
-
-TEST(AdbCounterTest, rollover_first_byte) {
- Counter<kTestCounterSize> counter;
- memset(counter.data(), 0, counter.size());
- counter[counter.size() - 1] = 0xFF;
- counter.Increase();
- EXPECT_EQ(0, counter[counter.size() - 1]);
- EXPECT_EQ(1, counter[counter.size() - 2]);
- EXPECT_EQ(0, memcmp(counter.data(), kZeroes, counter.size() - 2));
-}
-
-TEST(AdbCounterTest, multiple_rollover) {
- Counter<kTestCounterSize> counter;
- memset(counter.data(), 0xFF, counter.size());
- memset(counter.data(), 0, counter.size() - 3);
- counter.Increase();
- EXPECT_EQ(0, counter[counter.size() - 5]);
- EXPECT_EQ(1, counter[counter.size() - 4]);
- EXPECT_EQ(0, counter[counter.size() - 3]);
- EXPECT_EQ(0, counter[counter.size() - 2]);
- EXPECT_EQ(0, counter[counter.size() - 1]);
-}
-
-TEST(AdbCounterTest, full_rollover) {
- Counter<kTestCounterSize> counter;
- memset(counter.data(), 0xFF, counter.size());
- counter.Increase();
- EXPECT_EQ(0, memcmp(counter.data(), kZeroes, counter.size()));
-}
-
-} // namespace pairing
-} // namespace adb
diff --git a/adb/pairing_connection/Android.bp b/adb/pairing_connection/Android.bp
index bcde7b1..9595511 100644
--- a/adb/pairing_connection/Android.bp
+++ b/adb/pairing_connection/Android.bp
@@ -41,6 +41,9 @@
"//art:__subpackages__",
"//system/core/adb:__subpackages__",
"//frameworks/base/services:__subpackages__",
+
+ // This needs to be visible to minadbd, even though it's removed via exclude_shared_libs.
+ "//bootable/recovery/minadbd:__subpackages__",
],
apex_available: [
"com.android.adbd",
@@ -81,7 +84,7 @@
static_libs: [
"libadb_protos",
// Statically link libadb_tls_connection because it is not
- // ABI-stable.
+ // ABI-stable.
"libadb_tls_connection",
"libprotobuf-cpp-lite",
],
@@ -130,7 +133,6 @@
"//frameworks/base/services:__subpackages__",
],
- host_supported: true,
recovery_available: false,
stl: "libc++_static",
diff --git a/adb/pairing_connection/pairing_connection.cpp b/adb/pairing_connection/pairing_connection.cpp
index a26a6b4..ffe49a9 100644
--- a/adb/pairing_connection/pairing_connection.cpp
+++ b/adb/pairing_connection/pairing_connection.cpp
@@ -278,13 +278,13 @@
if (fd < 0) {
return false;
}
+ fd_.reset(fd);
State expected = State::Ready;
if (!state_.compare_exchange_strong(expected, State::ExchangingMsgs)) {
return false;
}
- fd_.reset(fd);
cb_ = cb;
opaque_ = opaque;
diff --git a/adb/proto/Android.bp b/adb/proto/Android.bp
index a7e5d9c..086d10e 100644
--- a/adb/proto/Android.bp
+++ b/adb/proto/Android.bp
@@ -41,6 +41,9 @@
visibility: [
"//system/core/adb:__subpackages__",
+
+ // This needs to be visible to minadbd, even though it's removed via exclude_shared_libs.
+ "//bootable/recovery/minadbd:__subpackages__",
],
stl: "libc++_static",
@@ -68,3 +71,64 @@
"//apex_available:platform",
],
}
+
+cc_defaults {
+ name: "libapp_processes_protos_defaults",
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Wthread-safety",
+ "-Werror",
+ ],
+
+ compile_multilib: "both",
+
+ srcs: [
+ "app_processes.proto",
+ ],
+ target: {
+ windows: {
+ compile_multilib: "first",
+ enabled: true,
+ },
+ },
+
+ visibility: [
+ "//system/core/adb:__subpackages__",
+
+ // This needs to be visible to minadbd, even though it's removed via exclude_shared_libs.
+ "//bootable/recovery/minadbd:__subpackages__",
+ ],
+
+ stl: "libc++_static",
+
+ apex_available: [
+ "com.android.adbd",
+ "test_com.android.adbd",
+ ],
+}
+
+cc_library {
+ name: "libapp_processes_protos_lite",
+ defaults: ["libapp_processes_protos_defaults"],
+
+ apex_available: ["//apex_available:platform"],
+
+ proto: {
+ export_proto_headers: true,
+ type: "lite",
+ },
+
+ host_supported: true,
+ recovery_available: true,
+}
+
+cc_library_host_static {
+ name: "libapp_processes_protos_full",
+ defaults: ["libapp_processes_protos_defaults"],
+
+ proto: {
+ export_proto_headers: true,
+ type: "full",
+ },
+}
diff --git a/init/service_lock.cpp b/adb/proto/app_processes.proto
similarity index 64%
copy from init/service_lock.cpp
copy to adb/proto/app_processes.proto
index 404d439..1183645 100644
--- a/init/service_lock.cpp
+++ b/adb/proto/app_processes.proto
@@ -14,12 +14,20 @@
* limitations under the License.
*/
-#include "service_lock.h"
+syntax = "proto3";
-namespace android {
-namespace init {
+option java_package = "com.android.server.adb.protos";
+option java_outer_classname = "AppProcessesProto";
-RecursiveMutex service_lock;
+package adb.proto;
-} // namespace init
-} // namespace android
+message ProcessEntry {
+ int64 pid = 1;
+ bool debuggable = 2;
+ bool profileable = 3;
+ string architecture = 4;
+}
+
+message AppProcesses {
+ repeated ProcessEntry process = 1;
+}
diff --git a/adb/services.cpp b/adb/services.cpp
index 853d658..19a9030 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -95,56 +95,6 @@
}
#if ADB_HOST
-struct state_info {
- TransportType transport_type;
- std::string serial;
- TransportId transport_id;
- ConnectionState state;
-};
-
-static void wait_for_state(unique_fd fd, state_info* sinfo) {
- D("wait_for_state %d", sinfo->state);
-
- while (true) {
- bool is_ambiguous = false;
- std::string error = "unknown error";
- const char* serial = sinfo->serial.length() ? sinfo->serial.c_str() : nullptr;
- atransport* t = acquire_one_transport(sinfo->transport_type, serial, sinfo->transport_id,
- &is_ambiguous, &error);
- if (sinfo->state == kCsOffline) {
- // wait-for-disconnect uses kCsOffline, we don't actually want to wait for 'offline'.
- if (t == nullptr) {
- SendOkay(fd);
- break;
- }
- } else if (t != nullptr &&
- (sinfo->state == kCsAny || sinfo->state == t->GetConnectionState())) {
- SendOkay(fd);
- break;
- }
-
- if (!is_ambiguous) {
- adb_pollfd pfd = {.fd = fd.get(), .events = POLLIN};
- int rc = adb_poll(&pfd, 1, 100);
- if (rc < 0) {
- SendFail(fd, error);
- break;
- } else if (rc > 0 && (pfd.revents & POLLHUP) != 0) {
- // The other end of the socket is closed, probably because the other side was
- // terminated, bail out.
- break;
- }
-
- // Try again...
- } else {
- SendFail(fd, error);
- break;
- }
- }
-
- D("wait_for_state is done");
-}
-
void connect_emulator(const std::string& port_spec, std::string* response) {
std::vector<std::string> pieces = android::base::Split(port_spec, ",");
if (pieces.size() != 2) {
@@ -201,6 +151,91 @@
adb_wifi_pair_device(host, password, response);
SendProtocolString(fd.get(), response);
}
+
+static void wait_service(unique_fd fd, std::string serial, TransportId transport_id,
+ std::string spec) {
+ std::vector<std::string> components = android::base::Split(spec, "-");
+ if (components.size() < 2) {
+ SendFail(fd, "short wait-for-: " + spec);
+ return;
+ }
+
+ TransportType transport_type;
+ if (components[0] == "local") {
+ transport_type = kTransportLocal;
+ } else if (components[0] == "usb") {
+ transport_type = kTransportUsb;
+ } else if (components[0] == "any") {
+ transport_type = kTransportAny;
+ } else {
+ SendFail(fd, "bad wait-for- transport: " + spec);
+ return;
+ }
+
+ std::vector<ConnectionState> states;
+ for (size_t i = 1; i < components.size(); ++i) {
+ if (components[i] == "device") {
+ states.push_back(kCsDevice);
+ } else if (components[i] == "recovery") {
+ states.push_back(kCsRecovery);
+ } else if (components[i] == "rescue") {
+ states.push_back(kCsRescue);
+ } else if (components[i] == "sideload") {
+ states.push_back(kCsSideload);
+ } else if (components[i] == "bootloader") {
+ states.push_back(kCsBootloader);
+ } else if (components[i] == "any") {
+ states.push_back(kCsAny);
+ } else if (components[i] == "disconnect") {
+ states.push_back(kCsOffline);
+ } else {
+ SendFail(fd, "bad wait-for- state: " + spec);
+ return;
+ }
+ }
+
+ while (true) {
+ bool is_ambiguous = false;
+ std::string error = "unknown error";
+ atransport* t =
+ acquire_one_transport(transport_type, !serial.empty() ? serial.c_str() : nullptr,
+ transport_id, &is_ambiguous, &error);
+
+ for (const auto& state : states) {
+ if (state == kCsOffline) {
+ // Special case for wait-for-disconnect:
+ // We want to wait for USB devices to completely disappear, but TCP devices can
+ // go into the offline state, since we automatically reconnect.
+ if (!t) {
+ SendOkay(fd);
+ return;
+ } else if (!t->GetUsbHandle()) {
+ SendOkay(fd);
+ return;
+ }
+ } else {
+ if (t && (state == kCsAny || state == t->GetConnectionState())) {
+ SendOkay(fd);
+ return;
+ }
+ }
+ }
+
+ if (is_ambiguous) {
+ SendFail(fd, error);
+ return;
+ }
+
+ // Sleep before retrying.
+ adb_pollfd pfd = {.fd = fd.get(), .events = POLLIN};
+ if (adb_poll(&pfd, 1, 100) != 0) {
+ // The other end of the socket is closed, probably because the
+ // client terminated. Bail out.
+ SendFail(fd, error);
+ return;
+ }
+ }
+}
#endif
#if ADB_HOST
@@ -211,45 +246,10 @@
} else if (name == "track-devices-l") {
return create_device_tracker(true);
} else if (android::base::ConsumePrefix(&name, "wait-for-")) {
- std::shared_ptr<state_info> sinfo = std::make_shared<state_info>();
- if (sinfo == nullptr) {
- fprintf(stderr, "couldn't allocate state_info: %s", strerror(errno));
- return nullptr;
- }
-
- sinfo->serial = serial;
- sinfo->transport_id = transport_id;
-
- if (android::base::ConsumePrefix(&name, "local")) {
- sinfo->transport_type = kTransportLocal;
- } else if (android::base::ConsumePrefix(&name, "usb")) {
- sinfo->transport_type = kTransportUsb;
- } else if (android::base::ConsumePrefix(&name, "any")) {
- sinfo->transport_type = kTransportAny;
- } else {
- return nullptr;
- }
-
- if (name == "-device") {
- sinfo->state = kCsDevice;
- } else if (name == "-recovery") {
- sinfo->state = kCsRecovery;
- } else if (name == "-rescue") {
- sinfo->state = kCsRescue;
- } else if (name == "-sideload") {
- sinfo->state = kCsSideload;
- } else if (name == "-bootloader") {
- sinfo->state = kCsBootloader;
- } else if (name == "-any") {
- sinfo->state = kCsAny;
- } else if (name == "-disconnect") {
- sinfo->state = kCsOffline;
- } else {
- return nullptr;
- }
-
- unique_fd fd = create_service_thread(
- "wait", [sinfo](unique_fd fd) { wait_for_state(std::move(fd), sinfo.get()); });
+ std::string spec(name);
+ unique_fd fd =
+ create_service_thread("wait", std::bind(wait_service, std::placeholders::_1,
+ std::string(serial), transport_id, spec));
return create_local_socket(std::move(fd));
} else if (android::base::ConsumePrefix(&name, "connect:")) {
std::string host(name);
diff --git a/adb/socket.h b/adb/socket.h
index 4276851..0623204 100644
--- a/adb/socket.h
+++ b/adb/socket.h
@@ -108,7 +108,10 @@
asocket *create_remote_socket(unsigned id, atransport *t);
void connect_to_remote(asocket* s, std::string_view destination);
+
+#if ADB_HOST
void connect_to_smartsocket(asocket *s);
+#endif
// Internal functions that are only made available here for testing purposes.
namespace internal {
diff --git a/adb/socket_spec.cpp b/adb/socket_spec.cpp
index d17036c..5cad70d 100644
--- a/adb/socket_spec.cpp
+++ b/adb/socket_spec.cpp
@@ -30,6 +30,7 @@
#include "adb.h"
#include "adb_utils.h"
+#include "adb_wifi.h"
#include "sysdeps.h"
using namespace std::string_literals;
@@ -103,12 +104,6 @@
if (!android::base::ParseNetAddress(addr, &hostname_value, &port_value, serial, error)) {
return false;
}
-
- if (port_value == -1) {
- *error = "missing port in specification: ";
- *error += spec;
- return false;
- }
}
if (hostname) {
@@ -201,7 +196,24 @@
fd->reset(network_loopback_client(port_value, SOCK_STREAM, error));
} else {
#if ADB_HOST
- fd->reset(network_connect(hostname, port_value, SOCK_STREAM, 0, error));
+ // Check if the address is an mdns service we can connect to.
+ if (auto mdns_info = mdns_get_connect_service_info(address.substr(4));
+ mdns_info != std::nullopt) {
+ fd->reset(network_connect(mdns_info->addr, mdns_info->port, SOCK_STREAM, 0, error));
+ if (fd->get() != -1) {
+ // TODO(joshuaduong): We still show the ip address for the serial. Change it to
+ // use the mdns instance name, so we can adjust to address changes on
+ // reconnects.
+ port_value = mdns_info->port;
+ if (serial) {
+ *serial = android::base::StringPrintf("%s.%s",
+ mdns_info->service_name.c_str(),
+ mdns_info->service_type.c_str());
+ }
+ }
+ } else {
+ fd->reset(network_connect(hostname, port_value, SOCK_STREAM, 0, error));
+ }
#else
// Disallow arbitrary connections in adbd.
*error = "adbd does not support arbitrary tcp connections";
diff --git a/adb/socket_spec_test.cpp b/adb/socket_spec_test.cpp
index e9d5270..e83c34c 100644
--- a/adb/socket_spec_test.cpp
+++ b/adb/socket_spec_test.cpp
@@ -24,6 +24,13 @@
#include <android-base/stringprintf.h>
#include <gtest/gtest.h>
+TEST(socket_spec, parse_tcp_socket_spec_failure) {
+ std::string hostname, error, serial;
+ int port;
+ EXPECT_FALSE(parse_tcp_socket_spec("sneakernet:5037", &hostname, &port, &serial, &error));
+ EXPECT_TRUE(error.find("sneakernet") != std::string::npos);
+}
+
TEST(socket_spec, parse_tcp_socket_spec_just_port) {
std::string hostname, error, serial;
int port;
@@ -134,6 +141,19 @@
EXPECT_NE(client_fd.get(), -1);
}
+TEST(socket_spec, socket_spec_connect_failure) {
+ std::string error, serial;
+ int port;
+ unique_fd client_fd;
+ EXPECT_FALSE(socket_spec_connect(&client_fd, "tcp:", &port, &serial, &error));
+ EXPECT_FALSE(socket_spec_connect(&client_fd, "acceptfd:", &port, &serial, &error));
+ EXPECT_FALSE(socket_spec_connect(&client_fd, "vsock:", &port, &serial, &error));
+ EXPECT_FALSE(socket_spec_connect(&client_fd, "vsock:x", &port, &serial, &error));
+ EXPECT_FALSE(socket_spec_connect(&client_fd, "vsock:5", &port, &serial, &error));
+ EXPECT_FALSE(socket_spec_connect(&client_fd, "vsock:5:x", &port, &serial, &error));
+ EXPECT_FALSE(socket_spec_connect(&client_fd, "sneakernet:", &port, &serial, &error));
+}
+
TEST(socket_spec, socket_spec_listen_connect_localfilesystem) {
std::string error, serial;
int port;
@@ -152,3 +172,16 @@
EXPECT_NE(client_fd.get(), -1);
}
}
+
+TEST(socket_spec, is_socket_spec) {
+ EXPECT_TRUE(is_socket_spec("tcp:blah"));
+ EXPECT_TRUE(is_socket_spec("acceptfd:blah"));
+ EXPECT_TRUE(is_socket_spec("local:blah"));
+ EXPECT_TRUE(is_socket_spec("localreserved:blah"));
+}
+
+TEST(socket_spec, is_local_socket_spec) {
+ EXPECT_TRUE(is_local_socket_spec("local:blah"));
+ EXPECT_TRUE(is_local_socket_spec("tcp:localhost"));
+ EXPECT_FALSE(is_local_socket_spec("tcp:www.google.com"));
+}
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 423af67..13a4737 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -520,6 +520,7 @@
send_packet(p, s->transport);
}
+#if ADB_HOST
/* this is used by magic sockets to rig local sockets to
send the go-ahead message when they connect */
static void local_socket_ready_notify(asocket* s) {
@@ -584,8 +585,6 @@
return n;
}
-#if ADB_HOST
-
namespace internal {
// Parses a host service string of the following format:
@@ -714,15 +713,11 @@
} // namespace internal
-#endif // ADB_HOST
-
static int smart_socket_enqueue(asocket* s, apacket::payload_type data) {
-#if ADB_HOST
std::string_view service;
std::string_view serial;
TransportId transport_id = 0;
TransportType type = kTransportAny;
-#endif
D("SS(%d): enqueue %zu", s->id, data.size());
@@ -755,7 +750,6 @@
D("SS(%d): '%s'", s->id, (char*)(s->smart_socket_data.data() + 4));
-#if ADB_HOST
service = std::string_view(s->smart_socket_data).substr(4);
// TODO: These should be handled in handle_host_request.
@@ -841,16 +835,6 @@
s2->ready(s2);
return 0;
}
-#else /* !ADB_HOST */
- if (s->transport == nullptr) {
- std::string error_msg = "unknown failure";
- s->transport = acquire_one_transport(kTransportAny, nullptr, 0, nullptr, &error_msg);
- if (s->transport == nullptr) {
- SendFail(s->peer->fd, error_msg);
- goto fail;
- }
- }
-#endif
if (!s->transport) {
SendFail(s->peer->fd, "device offline (no transport)");
@@ -922,6 +906,7 @@
ss->peer = s;
s->ready(s);
}
+#endif
size_t asocket::get_max_payload() const {
size_t max_payload = MAX_PAYLOAD;
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 4efbc02..7326ab1 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -14,11 +14,11 @@
* limitations under the License.
*/
+#pragma once
+
/* this file contains system-dependent definitions used by ADB
* they're related to threads, sockets and file descriptors
*/
-#ifndef _ADB_SYSDEPS_H
-#define _ADB_SYSDEPS_H
#ifdef __CYGWIN__
# undef _WIN32
@@ -42,6 +42,12 @@
#include "sysdeps/network.h"
#include "sysdeps/stat.h"
+#if defined(__APPLE__)
+static inline void* mempcpy(void* dst, const void* src, size_t n) {
+ return static_cast<char*>(memcpy(dst, src, n)) + n;
+}
+#endif
+
#ifdef _WIN32
// Clang-only nullability specifiers
@@ -70,13 +76,13 @@
#define OS_PATH_SEPARATOR_STR "\\"
#define ENV_PATH_SEPARATOR_STR ";"
-static __inline__ bool adb_is_separator(char c) {
+static inline bool adb_is_separator(char c) {
return c == '\\' || c == '/';
}
extern int adb_thread_setname(const std::string& name);
-static __inline__ void close_on_exec(borrowed_fd fd) {
+static inline void close_on_exec(borrowed_fd fd) {
/* nothing really */
}
@@ -107,7 +113,7 @@
extern int adb_getlogin_r(char* buf, size_t bufsize);
// See the comments for the !defined(_WIN32) version of unix_close().
-static __inline__ int unix_close(int fd) {
+static inline int unix_close(int fd) {
return close(fd);
}
#undef close
@@ -117,7 +123,7 @@
extern int unix_read_interruptible(borrowed_fd fd, void* buf, size_t len);
// See the comments for the !defined(_WIN32) version of unix_read().
-static __inline__ int unix_read(borrowed_fd fd, void* buf, size_t len) {
+static inline int unix_read(borrowed_fd fd, void* buf, size_t len) {
return TEMP_FAILURE_RETRY(unix_read_interruptible(fd, buf, len));
}
@@ -128,7 +134,7 @@
#define pread ___xxx_pread
// See the comments for the !defined(_WIN32) version of unix_write().
-static __inline__ int unix_write(borrowed_fd fd, const void* buf, size_t len) {
+static inline int unix_write(borrowed_fd fd, const void* buf, size_t len) {
return write(fd.get(), buf, len);
}
#undef write
@@ -138,14 +144,14 @@
#define pwrite ___xxx_pwrite
// See the comments for the !defined(_WIN32) version of unix_lseek().
-static __inline__ int unix_lseek(borrowed_fd fd, int pos, int where) {
+static inline int unix_lseek(borrowed_fd fd, int pos, int where) {
return lseek(fd.get(), pos, where);
}
#undef lseek
#define lseek ___xxx_lseek
// See the comments for the !defined(_WIN32) version of adb_open_mode().
-static __inline__ int adb_open_mode(const char* path, int options, int mode) {
+static inline int adb_open_mode(const char* path, int options, int mode) {
return adb_open(path, options);
}
@@ -202,7 +208,7 @@
extern int adb_poll(adb_pollfd* fds, size_t nfds, int timeout);
#define poll ___xxx_poll
-static __inline__ int adb_is_absolute_host_path(const char* path) {
+static inline int adb_is_absolute_host_path(const char* path) {
return isalpha(path[0]) && path[1] == ':' && path[2] == '\\';
}
@@ -276,6 +282,7 @@
class Process {
public:
constexpr explicit Process(HANDLE h = nullptr) : h_(h) {}
+ constexpr Process(Process&& other) : h_(std::exchange(other.h_, nullptr)) {}
~Process() { close(); }
constexpr explicit operator bool() const { return h_ != nullptr; }
@@ -292,6 +299,8 @@
}
private:
+ DISALLOW_COPY_AND_ASSIGN(Process);
+
void close() {
if (*this) {
::CloseHandle(h_);
@@ -383,15 +392,15 @@
#define OS_PATH_SEPARATOR_STR "/"
#define ENV_PATH_SEPARATOR_STR ":"
-static __inline__ bool adb_is_separator(char c) {
+static inline bool adb_is_separator(char c) {
return c == '/';
}
-static __inline__ int get_fd_flags(borrowed_fd fd) {
+static inline int get_fd_flags(borrowed_fd fd) {
return fcntl(fd.get(), F_GETFD);
}
-static __inline__ void close_on_exec(borrowed_fd fd) {
+static inline void close_on_exec(borrowed_fd fd) {
int flags = get_fd_flags(fd);
if (flags >= 0 && (flags & FD_CLOEXEC) == 0) {
fcntl(fd.get(), F_SETFD, flags | FD_CLOEXEC);
@@ -407,7 +416,7 @@
// by unix_read(), unix_write(), unix_close()). Also, the C Runtime has
// configurable CR/LF translation which defaults to text mode, but is settable
// with _setmode().
-static __inline__ int unix_open(std::string_view path, int options, ...) {
+static inline int unix_open(std::string_view path, int options, ...) {
std::string zero_terminated(path.begin(), path.end());
if ((options & O_CREAT) == 0) {
return TEMP_FAILURE_RETRY(open(zero_terminated.c_str(), options));
@@ -423,7 +432,7 @@
// Similar to the two-argument adb_open(), but takes a mode parameter for file
// creation. See adb_open() for more info.
-static __inline__ int adb_open_mode(const char* pathname, int options, int mode) {
+static inline int adb_open_mode(const char* pathname, int options, int mode) {
return TEMP_FAILURE_RETRY(open(pathname, options, mode));
}
@@ -434,7 +443,7 @@
// sysdeps_win32.cpp) uses Windows native file I/O and bypasses the C Runtime
// and its CR/LF translation. The returned file descriptor should be used with
// adb_read(), adb_write(), adb_close(), etc.
-static __inline__ int adb_open(const char* pathname, int options) {
+static inline int adb_open(const char* pathname, int options) {
int fd = TEMP_FAILURE_RETRY(open(pathname, options));
if (fd < 0) return -1;
close_on_exec(fd);
@@ -443,7 +452,7 @@
#undef open
#define open ___xxx_open
-static __inline__ int adb_shutdown(borrowed_fd fd, int direction = SHUT_RDWR) {
+static inline int adb_shutdown(borrowed_fd fd, int direction = SHUT_RDWR) {
return shutdown(fd.get(), direction);
}
@@ -453,7 +462,7 @@
// Closes a file descriptor that came from adb_open() or adb_open_mode(), but
// not designed to take a file descriptor from unix_open(). See the comments
// for adb_open() for more info.
-__inline__ int adb_close(int fd) {
+inline int adb_close(int fd) {
return close(fd);
}
#undef close
@@ -462,23 +471,23 @@
// On Windows, ADB has an indirection layer for file descriptors. If we get a
// Win32 SOCKET object from an external library, we have to map it in to that
// indirection layer, which this does.
-__inline__ int adb_register_socket(int s) {
+inline int adb_register_socket(int s) {
return s;
}
-static __inline__ int adb_gethostname(char* name, size_t len) {
+static inline int adb_gethostname(char* name, size_t len) {
return gethostname(name, len);
}
-static __inline__ int adb_getlogin_r(char* buf, size_t bufsize) {
+static inline int adb_getlogin_r(char* buf, size_t bufsize) {
return getlogin_r(buf, bufsize);
}
-static __inline__ int adb_read(borrowed_fd fd, void* buf, size_t len) {
+static inline int adb_read(borrowed_fd fd, void* buf, size_t len) {
return TEMP_FAILURE_RETRY(read(fd.get(), buf, len));
}
-static __inline__ int adb_pread(borrowed_fd fd, void* buf, size_t len, off64_t offset) {
+static inline int adb_pread(borrowed_fd fd, void* buf, size_t len, off64_t offset) {
#if defined(__APPLE__)
return TEMP_FAILURE_RETRY(pread(fd.get(), buf, len, offset));
#else
@@ -487,7 +496,7 @@
}
// Like unix_read(), but does not handle EINTR.
-static __inline__ int unix_read_interruptible(borrowed_fd fd, void* buf, size_t len) {
+static inline int unix_read_interruptible(borrowed_fd fd, void* buf, size_t len) {
return read(fd.get(), buf, len);
}
@@ -496,11 +505,11 @@
#undef pread
#define pread ___xxx_pread
-static __inline__ int adb_write(borrowed_fd fd, const void* buf, size_t len) {
+static inline int adb_write(borrowed_fd fd, const void* buf, size_t len) {
return TEMP_FAILURE_RETRY(write(fd.get(), buf, len));
}
-static __inline__ int adb_pwrite(int fd, const void* buf, size_t len, off64_t offset) {
+static inline int adb_pwrite(int fd, const void* buf, size_t len, off64_t offset) {
#if defined(__APPLE__)
return TEMP_FAILURE_RETRY(pwrite(fd, buf, len, offset));
#else
@@ -513,7 +522,7 @@
#undef pwrite
#define pwrite ___xxx_pwrite
-static __inline__ int64_t adb_lseek(borrowed_fd fd, int64_t pos, int where) {
+static inline int64_t adb_lseek(borrowed_fd fd, int64_t pos, int where) {
#if defined(__APPLE__)
return lseek(fd.get(), pos, where);
#else
@@ -523,13 +532,13 @@
#undef lseek
#define lseek ___xxx_lseek
-static __inline__ int adb_unlink(const char* path) {
+static inline int adb_unlink(const char* path) {
return unlink(path);
}
#undef unlink
#define unlink ___xxx_unlink
-static __inline__ int adb_creat(const char* path, int mode) {
+static inline int adb_creat(const char* path, int mode) {
int fd = TEMP_FAILURE_RETRY(creat(path, mode));
if (fd < 0) return -1;
@@ -540,7 +549,7 @@
#undef creat
#define creat ___xxx_creat
-static __inline__ int unix_isatty(borrowed_fd fd) {
+static inline int unix_isatty(borrowed_fd fd) {
return isatty(fd.get());
}
#define isatty ___xxx_isatty
@@ -567,8 +576,8 @@
int network_connect(const std::string& host, int port, int type, int timeout, std::string* error);
-static __inline__ int adb_socket_accept(borrowed_fd serverfd, struct sockaddr* addr,
- socklen_t* addrlen) {
+static inline int adb_socket_accept(borrowed_fd serverfd, struct sockaddr* addr,
+ socklen_t* addrlen) {
int fd;
fd = TEMP_FAILURE_RETRY(accept(serverfd.get(), addr, addrlen));
@@ -597,7 +606,7 @@
#define unix_lseek adb_lseek
#define unix_close adb_close
-static __inline__ int adb_thread_setname(const std::string& name) {
+static inline int adb_thread_setname(const std::string& name) {
#ifdef __APPLE__
return pthread_setname_np(name.c_str());
#else
@@ -610,19 +619,19 @@
#endif
}
-static __inline__ int adb_setsockopt(borrowed_fd fd, int level, int optname, const void* optval,
- socklen_t optlen) {
+static inline int adb_setsockopt(borrowed_fd fd, int level, int optname, const void* optval,
+ socklen_t optlen) {
return setsockopt(fd.get(), level, optname, optval, optlen);
}
#undef setsockopt
#define setsockopt ___xxx_setsockopt
-static __inline__ int unix_socketpair(int d, int type, int protocol, int sv[2]) {
+static inline int unix_socketpair(int d, int type, int protocol, int sv[2]) {
return socketpair(d, type, protocol, sv);
}
-static __inline__ int adb_socketpair(int sv[2]) {
+static inline int adb_socketpair(int sv[2]) {
int rc;
rc = unix_socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
@@ -637,35 +646,41 @@
#define socketpair ___xxx_socketpair
typedef struct pollfd adb_pollfd;
-static __inline__ int adb_poll(adb_pollfd* fds, size_t nfds, int timeout) {
+static inline int adb_poll(adb_pollfd* fds, size_t nfds, int timeout) {
return TEMP_FAILURE_RETRY(poll(fds, nfds, timeout));
}
#define poll ___xxx_poll
-static __inline__ int adb_mkdir(const std::string& path, int mode) {
+static inline int adb_mkdir(const std::string& path, int mode) {
return mkdir(path.c_str(), mode);
}
#undef mkdir
#define mkdir ___xxx_mkdir
-static __inline__ int adb_rename(const char* oldpath, const char* newpath) {
+static inline int adb_rename(const char* oldpath, const char* newpath) {
return rename(oldpath, newpath);
}
-static __inline__ int adb_is_absolute_host_path(const char* path) {
+static inline int adb_is_absolute_host_path(const char* path) {
return path[0] == '/';
}
-static __inline__ int adb_get_os_handle(borrowed_fd fd) {
+static inline int adb_get_os_handle(borrowed_fd fd) {
return fd.get();
}
+static inline int cast_handle_to_int(int fd) {
+ return fd;
+}
+
// A very simple wrapper over a launched child process
class Process {
public:
constexpr explicit Process(pid_t pid) : pid_(pid) {}
+ constexpr Process(Process&& other) : pid_(std::exchange(other.pid_, -1)) {}
+
constexpr explicit operator bool() const { return pid_ >= 0; }
void wait() {
@@ -682,6 +697,8 @@
}
private:
+ DISALLOW_COPY_AND_ASSIGN(Process);
+
pid_t pid_;
};
@@ -704,5 +721,3 @@
// Win32 defines ERROR, which we don't need, but which conflicts with google3 logging.
#undef ERROR
#endif
-
-#endif /* _ADB_SYSDEPS_H */
diff --git a/adb/test_adb.py b/adb/test_adb.py
index c872fb0..b9f0d54 100755
--- a/adb/test_adb.py
+++ b/adb/test_adb.py
@@ -25,6 +25,7 @@
import random
import select
import socket
+import string
import struct
import subprocess
import sys
@@ -32,6 +33,7 @@
import time
import unittest
import warnings
+from importlib import util
def find_open_port():
# Find an open port.
@@ -576,6 +578,169 @@
# If the power event was detected, the adb shell command should be broken very quickly.
self.assertLess(end - start, 2)
+"""Use 'adb mdns check' to see if mdns discovery is available."""
+def is_adb_mdns_available():
+ with adb_server() as server_port:
+ output = subprocess.check_output(["adb", "-P", str(server_port),
+ "mdns", "check"]).strip()
+ return output.startswith(b"mdns daemon version")
+
+"""Check if we have zeroconf python library installed"""
+def is_zeroconf_installed():
+ zeroconf_spec = util.find_spec("zeroconf")
+ return zeroconf_spec is not None
+
+@contextlib.contextmanager
+def zeroconf_context(ipversion):
+ from zeroconf import Zeroconf
+ """Context manager for a zeroconf instance
+
+ This creates a zeroconf instance and returns it.
+ """
+
+ try:
+ zeroconf = Zeroconf(ip_version=ipversion)
+ yield zeroconf
+ finally:
+ zeroconf.close()
+
+@contextlib.contextmanager
+def zeroconf_register_service(zeroconf_ctx, info):
+ """Context manager for a zeroconf service
+
+ Registers a service and unregisters it on cleanup. Returns the ServiceInfo
+ supplied.
+ """
+
+ try:
+ zeroconf_ctx.register_service(info)
+ yield info
+ finally:
+ zeroconf_ctx.unregister_service(info)
+
+"""Should match the service names listed in adb_mdns.h"""
+class MdnsTest:
+ """Tests for adb mdns."""
+
+ class Base(unittest.TestCase):
+ @staticmethod
+ def _mdns_services(port):
+ output = subprocess.check_output(["adb", "-P", str(port), "mdns", "services"])
+ return [x.split("\t") for x in output.decode("utf8").strip().splitlines()[1:]]
+
+ @staticmethod
+ def _devices(port):
+ output = subprocess.check_output(["adb", "-P", str(port), "devices"])
+ return [x.split("\t") for x in output.decode("utf8").strip().splitlines()[1:]]
+
+ @contextlib.contextmanager
+ def _adb_mdns_connect(self, server_port, mdns_instance, serial, should_connect):
+ """Context manager for an ADB connection.
+
+ This automatically disconnects when done with the connection.
+ """
+
+ output = subprocess.check_output(["adb", "-P", str(server_port), "connect", mdns_instance])
+ if should_connect:
+ self.assertEqual(output.strip(), "connected to {}".format(serial).encode("utf8"))
+ else:
+ self.assertTrue(output.startswith("failed to resolve host: '{}'"
+ .format(mdns_instance).encode("utf8")))
+
+ try:
+ yield
+ finally:
+ # Perform best-effort disconnection. Discard the output.
+ subprocess.Popen(["adb", "disconnect", serial],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE).communicate()
+
+
+ @unittest.skipIf(not is_zeroconf_installed(), "zeroconf library not installed")
+ def test_mdns_services_register_unregister(self):
+ """Ensure that `adb mdns services` correctly adds and removes a service
+ """
+ from zeroconf import IPVersion, ServiceInfo
+
+ with adb_server() as server_port:
+ output = subprocess.check_output(["adb", "-P", str(server_port),
+ "mdns", "services"]).strip()
+ self.assertTrue(output.startswith(b"List of discovered mdns services"))
+
+ """TODO(joshuaduong): Add ipv6 tests once we have it working in adb"""
+ """Register/Unregister a service"""
+ with zeroconf_context(IPVersion.V4Only) as zc:
+ serv_instance = "my_fake_test_service"
+ serv_type = "_" + self.service_name + "._tcp."
+ serv_ipaddr = socket.inet_aton("1.2.3.4")
+ serv_port = 12345
+ service_info = ServiceInfo(
+ serv_type + "local.",
+ name=serv_instance + "." + serv_type + "local.",
+ addresses=[serv_ipaddr],
+ port=serv_port)
+ with zeroconf_register_service(zc, service_info) as info:
+ """Give adb some time to register the service"""
+ time.sleep(1)
+ self.assertTrue(any((serv_instance in line and serv_type in line)
+ for line in MdnsTest._mdns_services(server_port)))
+
+ """Give adb some time to unregister the service"""
+ time.sleep(1)
+ self.assertFalse(any((serv_instance in line and serv_type in line)
+ for line in MdnsTest._mdns_services(server_port)))
+
+ @unittest.skipIf(not is_zeroconf_installed(), "zeroconf library not installed")
+ def test_mdns_connect(self):
+ """Ensure that `adb connect` by mdns instance name works (for non-pairing services)
+ """
+ from zeroconf import IPVersion, ServiceInfo
+
+ with adb_server() as server_port:
+ with zeroconf_context(IPVersion.V4Only) as zc:
+ serv_instance = "fakeadbd-" + ''.join(
+ random.choice(string.ascii_letters) for i in range(4))
+ serv_type = "_" + self.service_name + "._tcp."
+ serv_ipaddr = socket.inet_aton("127.0.0.1")
+ should_connect = self.service_name != "adb-tls-pairing"
+ with fake_adbd() as (port, _):
+ service_info = ServiceInfo(
+ serv_type + "local.",
+ name=serv_instance + "." + serv_type + "local.",
+ addresses=[serv_ipaddr],
+ port=port)
+ with zeroconf_register_service(zc, service_info) as info:
+ """Give adb some time to register the service"""
+ time.sleep(1)
+ self.assertTrue(any((serv_instance in line and serv_type in line)
+ for line in MdnsTest._mdns_services(server_port)))
+ full_name = '.'.join([serv_instance, serv_type])
+ with self._adb_mdns_connect(server_port, serv_instance, full_name,
+ should_connect):
+ if should_connect:
+ self.assertEqual(MdnsTest._devices(server_port),
+ [[full_name, "device"]])
+
+ """Give adb some time to unregister the service"""
+ time.sleep(1)
+ self.assertFalse(any((serv_instance in line and serv_type in line)
+ for line in MdnsTest._mdns_services(server_port)))
+
+
+@unittest.skipIf(not is_adb_mdns_available(), "mdns feature not available")
+class MdnsTestAdb(MdnsTest.Base):
+ service_name = "adb"
+
+
+@unittest.skipIf(not is_adb_mdns_available(), "mdns feature not available")
+class MdnsTestAdbTlsConnect(MdnsTest.Base):
+ service_name = "adb-tls-connect"
+
+
+@unittest.skipIf(not is_adb_mdns_available(), "mdns feature not available")
+class MdnsTestAdbTlsPairing(MdnsTest.Base):
+ service_name = "adb-tls-pairing"
+
def main():
"""Main entrypoint."""
diff --git a/adb/test_device.py b/adb/test_device.py
index 6a9ff89..c1caafc 100755
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -77,16 +77,18 @@
class DeviceTest(unittest.TestCase):
- def setUp(self):
- self.device = adb.get_device()
+ device = adb.get_device()
class AbbTest(DeviceTest):
def test_smoke(self):
- result = subprocess.run(['adb', 'abb'], capture_output=True)
- self.assertEqual(1, result.returncode)
- expected_output = b"cmd: No service specified; use -l to list all services\n"
- self.assertEqual(expected_output, result.stderr)
+ abb = subprocess.run(['adb', 'abb'], capture_output=True)
+ cmd = subprocess.run(['adb', 'shell', 'cmd'], capture_output=True)
+
+ # abb squashes all failures to 1.
+ self.assertEqual(abb.returncode == 0, cmd.returncode == 0)
+ self.assertEqual(abb.stdout, cmd.stdout)
+ self.assertEqual(abb.stderr, cmd.stderr)
class ForwardReverseTest(DeviceTest):
def _test_no_rebind(self, description, direction_list, direction,
@@ -753,535 +755,615 @@
return files
-class FileOperationsTest(DeviceTest):
- SCRATCH_DIR = '/data/local/tmp'
- DEVICE_TEMP_FILE = SCRATCH_DIR + '/adb_test_file'
- DEVICE_TEMP_DIR = SCRATCH_DIR + '/adb_test_dir'
+class FileOperationsTest:
+ class Base(DeviceTest):
+ SCRATCH_DIR = '/data/local/tmp'
+ DEVICE_TEMP_FILE = SCRATCH_DIR + '/adb_test_file'
+ DEVICE_TEMP_DIR = SCRATCH_DIR + '/adb_test_dir'
- def _verify_remote(self, checksum, remote_path):
- dev_md5, _ = self.device.shell([get_md5_prog(self.device),
- remote_path])[0].split()
- self.assertEqual(checksum, dev_md5)
+ def setUp(self):
+ self.previous_env = os.environ.get("ADB_COMPRESSION")
+ os.environ["ADB_COMPRESSION"] = self.compression
- def _verify_local(self, checksum, local_path):
- with open(local_path, 'rb') as host_file:
- host_md5 = compute_md5(host_file.read())
- self.assertEqual(host_md5, checksum)
+ def tearDown(self):
+ if self.previous_env is None:
+ del os.environ["ADB_COMPRESSION"]
+ else:
+ os.environ["ADB_COMPRESSION"] = self.previous_env
- def test_push(self):
- """Push a randomly generated file to specified device."""
- kbytes = 512
- tmp = tempfile.NamedTemporaryFile(mode='wb', delete=False)
- rand_str = os.urandom(1024 * kbytes)
- tmp.write(rand_str)
- tmp.close()
+ def _verify_remote(self, checksum, remote_path):
+ dev_md5, _ = self.device.shell([get_md5_prog(self.device),
+ remote_path])[0].split()
+ self.assertEqual(checksum, dev_md5)
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_FILE])
- self.device.push(local=tmp.name, remote=self.DEVICE_TEMP_FILE)
+ def _verify_local(self, checksum, local_path):
+ with open(local_path, 'rb') as host_file:
+ host_md5 = compute_md5(host_file.read())
+ self.assertEqual(host_md5, checksum)
- self._verify_remote(compute_md5(rand_str), self.DEVICE_TEMP_FILE)
- self.device.shell(['rm', '-f', self.DEVICE_TEMP_FILE])
+ def test_push(self):
+ """Push a randomly generated file to specified device."""
+ kbytes = 512
+ tmp = tempfile.NamedTemporaryFile(mode='wb', delete=False)
+ rand_str = os.urandom(1024 * kbytes)
+ tmp.write(rand_str)
+ tmp.close()
- os.remove(tmp.name)
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_FILE])
+ self.device.push(local=tmp.name, remote=self.DEVICE_TEMP_FILE)
- def test_push_dir(self):
- """Push a randomly generated directory of files to the device."""
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- self.device.shell(['mkdir', self.DEVICE_TEMP_DIR])
+ self._verify_remote(compute_md5(rand_str), self.DEVICE_TEMP_FILE)
+ self.device.shell(['rm', '-f', self.DEVICE_TEMP_FILE])
- try:
- host_dir = tempfile.mkdtemp()
+ os.remove(tmp.name)
- # Make sure the temp directory isn't setuid, or else adb will complain.
- os.chmod(host_dir, 0o700)
-
- # Create 32 random files.
- temp_files = make_random_host_files(in_dir=host_dir, num_files=32)
- self.device.push(host_dir, self.DEVICE_TEMP_DIR)
-
- for temp_file in temp_files:
- remote_path = posixpath.join(self.DEVICE_TEMP_DIR,
- os.path.basename(host_dir),
- temp_file.base_name)
- self._verify_remote(temp_file.checksum, remote_path)
+ def test_push_dir(self):
+ """Push a randomly generated directory of files to the device."""
self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- finally:
- if host_dir is not None:
- shutil.rmtree(host_dir)
+ self.device.shell(['mkdir', self.DEVICE_TEMP_DIR])
- def disabled_test_push_empty(self):
- """Push an empty directory to the device."""
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- self.device.shell(['mkdir', self.DEVICE_TEMP_DIR])
+ try:
+ host_dir = tempfile.mkdtemp()
- try:
- host_dir = tempfile.mkdtemp()
+ # Make sure the temp directory isn't setuid, or else adb will complain.
+ os.chmod(host_dir, 0o700)
- # Make sure the temp directory isn't setuid, or else adb will complain.
- os.chmod(host_dir, 0o700)
+ # Create 32 random files.
+ temp_files = make_random_host_files(in_dir=host_dir, num_files=32)
+ self.device.push(host_dir, self.DEVICE_TEMP_DIR)
- # Create an empty directory.
- empty_dir_path = os.path.join(host_dir, 'empty')
- os.mkdir(empty_dir_path);
+ for temp_file in temp_files:
+ remote_path = posixpath.join(self.DEVICE_TEMP_DIR,
+ os.path.basename(host_dir),
+ temp_file.base_name)
+ self._verify_remote(temp_file.checksum, remote_path)
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
- self.device.push(empty_dir_path, self.DEVICE_TEMP_DIR)
-
- remote_path = os.path.join(self.DEVICE_TEMP_DIR, "empty")
- test_empty_cmd = ["[", "-d", remote_path, "]"]
- rc, _, _ = self.device.shell_nocheck(test_empty_cmd)
-
- self.assertEqual(rc, 0)
+ def disabled_test_push_empty(self):
+ """Push an empty directory to the device."""
self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- finally:
- if host_dir is not None:
- shutil.rmtree(host_dir)
+ self.device.shell(['mkdir', self.DEVICE_TEMP_DIR])
- @unittest.skipIf(sys.platform == "win32", "symlinks require elevated privileges on windows")
- def test_push_symlink(self):
- """Push a symlink.
+ try:
+ host_dir = tempfile.mkdtemp()
- Bug: http://b/31491920
- """
- try:
- host_dir = tempfile.mkdtemp()
+ # Make sure the temp directory isn't setuid, or else adb will complain.
+ os.chmod(host_dir, 0o700)
- # Make sure the temp directory isn't setuid, or else adb will
- # complain.
- os.chmod(host_dir, 0o700)
+ # Create an empty directory.
+ empty_dir_path = os.path.join(host_dir, 'empty')
+ os.mkdir(empty_dir_path);
- with open(os.path.join(host_dir, 'foo'), 'w') as f:
- f.write('foo')
+ self.device.push(empty_dir_path, self.DEVICE_TEMP_DIR)
- symlink_path = os.path.join(host_dir, 'symlink')
- os.symlink('foo', symlink_path)
+ remote_path = os.path.join(self.DEVICE_TEMP_DIR, "empty")
+ test_empty_cmd = ["[", "-d", remote_path, "]"]
+ rc, _, _ = self.device.shell_nocheck(test_empty_cmd)
+
+ self.assertEqual(rc, 0)
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
+
+ @unittest.skipIf(sys.platform == "win32", "symlinks require elevated privileges on windows")
+ def test_push_symlink(self):
+ """Push a symlink.
+
+ Bug: http://b/31491920
+ """
+ try:
+ host_dir = tempfile.mkdtemp()
+
+ # Make sure the temp directory isn't setuid, or else adb will
+ # complain.
+ os.chmod(host_dir, 0o700)
+
+ with open(os.path.join(host_dir, 'foo'), 'w') as f:
+ f.write('foo')
+
+ symlink_path = os.path.join(host_dir, 'symlink')
+ os.symlink('foo', symlink_path)
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', self.DEVICE_TEMP_DIR])
+ self.device.push(symlink_path, self.DEVICE_TEMP_DIR)
+ rc, out, _ = self.device.shell_nocheck(
+ ['cat', posixpath.join(self.DEVICE_TEMP_DIR, 'symlink')])
+ self.assertEqual(0, rc)
+ self.assertEqual(out.strip(), 'foo')
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
+
+ def test_multiple_push(self):
+ """Push multiple files to the device in one adb push command.
+
+ Bug: http://b/25324823
+ """
self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
self.device.shell(['mkdir', self.DEVICE_TEMP_DIR])
- self.device.push(symlink_path, self.DEVICE_TEMP_DIR)
- rc, out, _ = self.device.shell_nocheck(
- ['cat', posixpath.join(self.DEVICE_TEMP_DIR, 'symlink')])
- self.assertEqual(0, rc)
- self.assertEqual(out.strip(), 'foo')
- finally:
- if host_dir is not None:
- shutil.rmtree(host_dir)
- def test_multiple_push(self):
- """Push multiple files to the device in one adb push command.
-
- Bug: http://b/25324823
- """
-
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- self.device.shell(['mkdir', self.DEVICE_TEMP_DIR])
-
- try:
- host_dir = tempfile.mkdtemp()
-
- # Create some random files and a subdirectory containing more files.
- temp_files = make_random_host_files(in_dir=host_dir, num_files=4)
-
- subdir = os.path.join(host_dir, 'subdir')
- os.mkdir(subdir)
- subdir_temp_files = make_random_host_files(in_dir=subdir,
- num_files=4)
-
- paths = [x.full_path for x in temp_files]
- paths.append(subdir)
- self.device._simple_call(['push'] + paths + [self.DEVICE_TEMP_DIR])
-
- for temp_file in temp_files:
- remote_path = posixpath.join(self.DEVICE_TEMP_DIR,
- temp_file.base_name)
- self._verify_remote(temp_file.checksum, remote_path)
-
- for subdir_temp_file in subdir_temp_files:
- remote_path = posixpath.join(self.DEVICE_TEMP_DIR,
- # BROKEN: http://b/25394682
- # 'subdir';
- temp_file.base_name)
- self._verify_remote(temp_file.checksum, remote_path)
-
-
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- finally:
- if host_dir is not None:
- shutil.rmtree(host_dir)
-
- @requires_non_root
- def test_push_error_reporting(self):
- """Make sure that errors that occur while pushing a file get reported
-
- Bug: http://b/26816782
- """
- with tempfile.NamedTemporaryFile() as tmp_file:
- tmp_file.write(b'\0' * 1024 * 1024)
- tmp_file.flush()
try:
- self.device.push(local=tmp_file.name, remote='/system/')
- self.fail('push should not have succeeded')
+ host_dir = tempfile.mkdtemp()
+
+ # Create some random files and a subdirectory containing more files.
+ temp_files = make_random_host_files(in_dir=host_dir, num_files=4)
+
+ subdir = os.path.join(host_dir, 'subdir')
+ os.mkdir(subdir)
+ subdir_temp_files = make_random_host_files(in_dir=subdir,
+ num_files=4)
+
+ paths = [x.full_path for x in temp_files]
+ paths.append(subdir)
+ self.device._simple_call(['push'] + paths + [self.DEVICE_TEMP_DIR])
+
+ for temp_file in temp_files:
+ remote_path = posixpath.join(self.DEVICE_TEMP_DIR,
+ temp_file.base_name)
+ self._verify_remote(temp_file.checksum, remote_path)
+
+ for subdir_temp_file in subdir_temp_files:
+ remote_path = posixpath.join(self.DEVICE_TEMP_DIR,
+ # BROKEN: http://b/25394682
+ # 'subdir';
+ temp_file.base_name)
+ self._verify_remote(temp_file.checksum, remote_path)
+
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
+
+ @requires_non_root
+ def test_push_error_reporting(self):
+ """Make sure that errors that occur while pushing a file get reported
+
+ Bug: http://b/26816782
+ """
+ with tempfile.NamedTemporaryFile() as tmp_file:
+ tmp_file.write(b'\0' * 1024 * 1024)
+ tmp_file.flush()
+ try:
+ self.device.push(local=tmp_file.name, remote='/system/')
+ self.fail('push should not have succeeded')
+ except subprocess.CalledProcessError as e:
+ output = e.output
+
+ self.assertTrue(b'Permission denied' in output or
+ b'Read-only file system' in output)
+
+ @requires_non_root
+ def test_push_directory_creation(self):
+ """Regression test for directory creation.
+
+ Bug: http://b/110953234
+ """
+ with tempfile.NamedTemporaryFile() as tmp_file:
+ tmp_file.write(b'\0' * 1024 * 1024)
+ tmp_file.flush()
+ remote_path = self.DEVICE_TEMP_DIR + '/test_push_directory_creation'
+ self.device.shell(['rm', '-rf', remote_path])
+
+ remote_path += '/filename'
+ self.device.push(local=tmp_file.name, remote=remote_path)
+
+ def disabled_test_push_multiple_slash_root(self):
+ """Regression test for pushing to //data/local/tmp.
+
+ Bug: http://b/141311284
+
+ Disabled because this broken on the adbd side as well: b/141943968
+ """
+ with tempfile.NamedTemporaryFile() as tmp_file:
+ tmp_file.write('\0' * 1024 * 1024)
+ tmp_file.flush()
+ remote_path = '/' + self.DEVICE_TEMP_DIR + '/test_push_multiple_slash_root'
+ self.device.shell(['rm', '-rf', remote_path])
+ self.device.push(local=tmp_file.name, remote=remote_path)
+
+ def _test_pull(self, remote_file, checksum):
+ tmp_write = tempfile.NamedTemporaryFile(mode='wb', delete=False)
+ tmp_write.close()
+ self.device.pull(remote=remote_file, local=tmp_write.name)
+ with open(tmp_write.name, 'rb') as tmp_read:
+ host_contents = tmp_read.read()
+ host_md5 = compute_md5(host_contents)
+ self.assertEqual(checksum, host_md5)
+ os.remove(tmp_write.name)
+
+ @requires_non_root
+ def test_pull_error_reporting(self):
+ self.device.shell(['touch', self.DEVICE_TEMP_FILE])
+ self.device.shell(['chmod', 'a-rwx', self.DEVICE_TEMP_FILE])
+
+ try:
+ output = self.device.pull(remote=self.DEVICE_TEMP_FILE, local='x')
except subprocess.CalledProcessError as e:
output = e.output
- self.assertTrue(b'Permission denied' in output or
- b'Read-only file system' in output)
+ self.assertIn(b'Permission denied', output)
- @requires_non_root
- def test_push_directory_creation(self):
- """Regression test for directory creation.
+ self.device.shell(['rm', '-f', self.DEVICE_TEMP_FILE])
- Bug: http://b/110953234
- """
- with tempfile.NamedTemporaryFile() as tmp_file:
- tmp_file.write(b'\0' * 1024 * 1024)
- tmp_file.flush()
- remote_path = self.DEVICE_TEMP_DIR + '/test_push_directory_creation'
- self.device.shell(['rm', '-rf', remote_path])
+ def test_pull(self):
+ """Pull a randomly generated file from specified device."""
+ kbytes = 512
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_FILE])
+ cmd = ['dd', 'if=/dev/urandom',
+ 'of={}'.format(self.DEVICE_TEMP_FILE), 'bs=1024',
+ 'count={}'.format(kbytes)]
+ self.device.shell(cmd)
+ dev_md5, _ = self.device.shell(
+ [get_md5_prog(self.device), self.DEVICE_TEMP_FILE])[0].split()
+ self._test_pull(self.DEVICE_TEMP_FILE, dev_md5)
+ self.device.shell_nocheck(['rm', self.DEVICE_TEMP_FILE])
- remote_path += '/filename'
- self.device.push(local=tmp_file.name, remote=remote_path)
+ def test_pull_dir(self):
+ """Pull a randomly generated directory of files from the device."""
+ try:
+ host_dir = tempfile.mkdtemp()
- def disabled_test_push_multiple_slash_root(self):
- """Regression test for pushing to //data/local/tmp.
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
- Bug: http://b/141311284
+ # Populate device directory with random files.
+ temp_files = make_random_device_files(
+ self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
- Disabled because this broken on the adbd side as well: b/141943968
- """
- with tempfile.NamedTemporaryFile() as tmp_file:
- tmp_file.write('\0' * 1024 * 1024)
- tmp_file.flush()
- remote_path = '/' + self.DEVICE_TEMP_DIR + '/test_push_multiple_slash_root'
- self.device.shell(['rm', '-rf', remote_path])
- self.device.push(local=tmp_file.name, remote=remote_path)
+ self.device.pull(remote=self.DEVICE_TEMP_DIR, local=host_dir)
- def _test_pull(self, remote_file, checksum):
- tmp_write = tempfile.NamedTemporaryFile(mode='wb', delete=False)
- tmp_write.close()
- self.device.pull(remote=remote_file, local=tmp_write.name)
- with open(tmp_write.name, 'rb') as tmp_read:
- host_contents = tmp_read.read()
- host_md5 = compute_md5(host_contents)
- self.assertEqual(checksum, host_md5)
- os.remove(tmp_write.name)
+ for temp_file in temp_files:
+ host_path = os.path.join(
+ host_dir, posixpath.basename(self.DEVICE_TEMP_DIR),
+ temp_file.base_name)
+ self._verify_local(temp_file.checksum, host_path)
- @requires_non_root
- def test_pull_error_reporting(self):
- self.device.shell(['touch', self.DEVICE_TEMP_FILE])
- self.device.shell(['chmod', 'a-rwx', self.DEVICE_TEMP_FILE])
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
- try:
- output = self.device.pull(remote=self.DEVICE_TEMP_FILE, local='x')
- except subprocess.CalledProcessError as e:
- output = e.output
+ def test_pull_dir_symlink(self):
+ """Pull a directory into a symlink to a directory.
- self.assertIn(b'Permission denied', output)
+ Bug: http://b/27362811
+ """
+ if os.name != 'posix':
+ raise unittest.SkipTest('requires POSIX')
- self.device.shell(['rm', '-f', self.DEVICE_TEMP_FILE])
+ try:
+ host_dir = tempfile.mkdtemp()
+ real_dir = os.path.join(host_dir, 'dir')
+ symlink = os.path.join(host_dir, 'symlink')
+ os.mkdir(real_dir)
+ os.symlink(real_dir, symlink)
- def test_pull(self):
- """Pull a randomly generated file from specified device."""
- kbytes = 512
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_FILE])
- cmd = ['dd', 'if=/dev/urandom',
- 'of={}'.format(self.DEVICE_TEMP_FILE), 'bs=1024',
- 'count={}'.format(kbytes)]
- self.device.shell(cmd)
- dev_md5, _ = self.device.shell(
- [get_md5_prog(self.device), self.DEVICE_TEMP_FILE])[0].split()
- self._test_pull(self.DEVICE_TEMP_FILE, dev_md5)
- self.device.shell_nocheck(['rm', self.DEVICE_TEMP_FILE])
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
- def test_pull_dir(self):
- """Pull a randomly generated directory of files from the device."""
- try:
- host_dir = tempfile.mkdtemp()
+ # Populate device directory with random files.
+ temp_files = make_random_device_files(
+ self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
+ self.device.pull(remote=self.DEVICE_TEMP_DIR, local=symlink)
- # Populate device directory with random files.
- temp_files = make_random_device_files(
- self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
+ for temp_file in temp_files:
+ host_path = os.path.join(
+ real_dir, posixpath.basename(self.DEVICE_TEMP_DIR),
+ temp_file.base_name)
+ self._verify_local(temp_file.checksum, host_path)
- self.device.pull(remote=self.DEVICE_TEMP_DIR, local=host_dir)
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
+ def test_pull_dir_symlink_collision(self):
+ """Pull a directory into a colliding symlink to directory."""
+ if os.name != 'posix':
+ raise unittest.SkipTest('requires POSIX')
+
+ try:
+ host_dir = tempfile.mkdtemp()
+ real_dir = os.path.join(host_dir, 'real')
+ tmp_dirname = os.path.basename(self.DEVICE_TEMP_DIR)
+ symlink = os.path.join(host_dir, tmp_dirname)
+ os.mkdir(real_dir)
+ os.symlink(real_dir, symlink)
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
+
+ # Populate device directory with random files.
+ temp_files = make_random_device_files(
+ self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
+
+ self.device.pull(remote=self.DEVICE_TEMP_DIR, local=host_dir)
+
+ for temp_file in temp_files:
+ host_path = os.path.join(real_dir, temp_file.base_name)
+ self._verify_local(temp_file.checksum, host_path)
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
+
+ def test_pull_dir_nonexistent(self):
+ """Pull a directory of files from the device to a nonexistent path."""
+ try:
+ host_dir = tempfile.mkdtemp()
+ dest_dir = os.path.join(host_dir, 'dest')
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
+
+ # Populate device directory with random files.
+ temp_files = make_random_device_files(
+ self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
+
+ self.device.pull(remote=self.DEVICE_TEMP_DIR, local=dest_dir)
+
+ for temp_file in temp_files:
+ host_path = os.path.join(dest_dir, temp_file.base_name)
+ self._verify_local(temp_file.checksum, host_path)
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
+
+ # selinux prevents adbd from accessing symlinks on /data/local/tmp.
+ def disabled_test_pull_symlink_dir(self):
+ """Pull a symlink to a directory of symlinks to files."""
+ try:
+ host_dir = tempfile.mkdtemp()
+
+ remote_dir = posixpath.join(self.DEVICE_TEMP_DIR, 'contents')
+ remote_links = posixpath.join(self.DEVICE_TEMP_DIR, 'links')
+ remote_symlink = posixpath.join(self.DEVICE_TEMP_DIR, 'symlink')
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', '-p', remote_dir, remote_links])
+ self.device.shell(['ln', '-s', remote_links, remote_symlink])
+
+ # Populate device directory with random files.
+ temp_files = make_random_device_files(
+ self.device, in_dir=remote_dir, num_files=32)
+
+ for temp_file in temp_files:
+ self.device.shell(
+ ['ln', '-s', '../contents/{}'.format(temp_file.base_name),
+ posixpath.join(remote_links, temp_file.base_name)])
+
+ self.device.pull(remote=remote_symlink, local=host_dir)
+
+ for temp_file in temp_files:
+ host_path = os.path.join(
+ host_dir, 'symlink', temp_file.base_name)
+ self._verify_local(temp_file.checksum, host_path)
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
+
+ def test_pull_empty(self):
+ """Pull a directory containing an empty directory from the device."""
+ try:
+ host_dir = tempfile.mkdtemp()
+
+ remote_empty_path = posixpath.join(self.DEVICE_TEMP_DIR, 'empty')
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', '-p', remote_empty_path])
+
+ self.device.pull(remote=remote_empty_path, local=host_dir)
+ self.assertTrue(os.path.isdir(os.path.join(host_dir, 'empty')))
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
+
+ def test_multiple_pull(self):
+ """Pull a randomly generated directory of files from the device."""
+
+ try:
+ host_dir = tempfile.mkdtemp()
+
+ subdir = posixpath.join(self.DEVICE_TEMP_DIR, 'subdir')
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', '-p', subdir])
+
+ # Create some random files and a subdirectory containing more files.
+ temp_files = make_random_device_files(
+ self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=4)
+
+ subdir_temp_files = make_random_device_files(
+ self.device, in_dir=subdir, num_files=4, prefix='subdir_')
+
+ paths = [x.full_path for x in temp_files]
+ paths.append(subdir)
+ self.device._simple_call(['pull'] + paths + [host_dir])
+
+ for temp_file in temp_files:
+ local_path = os.path.join(host_dir, temp_file.base_name)
+ self._verify_local(temp_file.checksum, local_path)
+
+ for subdir_temp_file in subdir_temp_files:
+ local_path = os.path.join(host_dir,
+ 'subdir',
+ subdir_temp_file.base_name)
+ self._verify_local(subdir_temp_file.checksum, local_path)
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
+
+ def verify_sync(self, device, temp_files, device_dir):
+ """Verifies that a list of temp files was synced to the device."""
+ # Confirm that every file on the device mirrors that on the host.
for temp_file in temp_files:
- host_path = os.path.join(
- host_dir, posixpath.basename(self.DEVICE_TEMP_DIR),
- temp_file.base_name)
- self._verify_local(temp_file.checksum, host_path)
+ device_full_path = posixpath.join(
+ device_dir, temp_file.base_name)
+ dev_md5, _ = device.shell(
+ [get_md5_prog(self.device), device_full_path])[0].split()
+ self.assertEqual(temp_file.checksum, dev_md5)
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- finally:
- if host_dir is not None:
- shutil.rmtree(host_dir)
+ def test_sync(self):
+ """Sync a host directory to the data partition."""
- def test_pull_dir_symlink(self):
- """Pull a directory into a symlink to a directory.
+ try:
+ base_dir = tempfile.mkdtemp()
- Bug: http://b/27362811
- """
- if os.name != 'posix':
- raise unittest.SkipTest('requires POSIX')
+ # Create mirror device directory hierarchy within base_dir.
+ full_dir_path = base_dir + self.DEVICE_TEMP_DIR
+ os.makedirs(full_dir_path)
- try:
- host_dir = tempfile.mkdtemp()
- real_dir = os.path.join(host_dir, 'dir')
- symlink = os.path.join(host_dir, 'symlink')
- os.mkdir(real_dir)
- os.symlink(real_dir, symlink)
+ # Create 32 random files within the host mirror.
+ temp_files = make_random_host_files(
+ in_dir=full_dir_path, num_files=32)
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
+ # Clean up any stale files on the device.
+ device = adb.get_device() # pylint: disable=no-member
+ device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- # Populate device directory with random files.
- temp_files = make_random_device_files(
- self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
+ old_product_out = os.environ.get('ANDROID_PRODUCT_OUT')
+ os.environ['ANDROID_PRODUCT_OUT'] = base_dir
+ device.sync('data')
+ if old_product_out is None:
+ del os.environ['ANDROID_PRODUCT_OUT']
+ else:
+ os.environ['ANDROID_PRODUCT_OUT'] = old_product_out
- self.device.pull(remote=self.DEVICE_TEMP_DIR, local=symlink)
+ self.verify_sync(device, temp_files, self.DEVICE_TEMP_DIR)
- for temp_file in temp_files:
- host_path = os.path.join(
- real_dir, posixpath.basename(self.DEVICE_TEMP_DIR),
- temp_file.base_name)
- self._verify_local(temp_file.checksum, host_path)
+ #self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if base_dir is not None:
+ shutil.rmtree(base_dir)
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- finally:
- if host_dir is not None:
- shutil.rmtree(host_dir)
+ def test_push_sync(self):
+ """Sync a host directory to a specific path."""
- def test_pull_dir_symlink_collision(self):
- """Pull a directory into a colliding symlink to directory."""
- if os.name != 'posix':
- raise unittest.SkipTest('requires POSIX')
+ try:
+ temp_dir = tempfile.mkdtemp()
+ temp_files = make_random_host_files(in_dir=temp_dir, num_files=32)
- try:
- host_dir = tempfile.mkdtemp()
- real_dir = os.path.join(host_dir, 'real')
- tmp_dirname = os.path.basename(self.DEVICE_TEMP_DIR)
- symlink = os.path.join(host_dir, tmp_dirname)
- os.mkdir(real_dir)
- os.symlink(real_dir, symlink)
+ device_dir = posixpath.join(self.DEVICE_TEMP_DIR, 'sync_src_dst')
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
+ # Clean up any stale files on the device.
+ device = adb.get_device() # pylint: disable=no-member
+ device.shell(['rm', '-rf', device_dir])
- # Populate device directory with random files.
- temp_files = make_random_device_files(
- self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
+ device.push(temp_dir, device_dir, sync=True)
- self.device.pull(remote=self.DEVICE_TEMP_DIR, local=host_dir)
+ self.verify_sync(device, temp_files, device_dir)
- for temp_file in temp_files:
- host_path = os.path.join(real_dir, temp_file.base_name)
- self._verify_local(temp_file.checksum, host_path)
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if temp_dir is not None:
+ shutil.rmtree(temp_dir)
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- finally:
- if host_dir is not None:
- shutil.rmtree(host_dir)
+ def test_push_dry_run_nonexistent_file(self):
+ """Push with dry run."""
- def test_pull_dir_nonexistent(self):
- """Pull a directory of files from the device to a nonexistent path."""
- try:
- host_dir = tempfile.mkdtemp()
- dest_dir = os.path.join(host_dir, 'dest')
+ for file_size in [8, 1024 * 1024]:
+ try:
+ device_dir = posixpath.join(self.DEVICE_TEMP_DIR, 'push_dry_run')
+ device_file = posixpath.join(device_dir, 'file')
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', '-p', device_dir])
- # Populate device directory with random files.
- temp_files = make_random_device_files(
- self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
+ host_dir = tempfile.mkdtemp()
+ host_file = posixpath.join(host_dir, 'file')
- self.device.pull(remote=self.DEVICE_TEMP_DIR, local=dest_dir)
+ with open(host_file, "w") as f:
+ f.write('x' * file_size)
- for temp_file in temp_files:
- host_path = os.path.join(dest_dir, temp_file.base_name)
- self._verify_local(temp_file.checksum, host_path)
+ self.device._simple_call(['push', '-n', host_file, device_file])
+ rc, _, _ = self.device.shell_nocheck(['[', '-e', device_file, ']'])
+ self.assertNotEqual(0, rc)
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- finally:
- if host_dir is not None:
- shutil.rmtree(host_dir)
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
- # selinux prevents adbd from accessing symlinks on /data/local/tmp.
- def disabled_test_pull_symlink_dir(self):
- """Pull a symlink to a directory of symlinks to files."""
- try:
- host_dir = tempfile.mkdtemp()
+ def test_push_dry_run_existent_file(self):
+ """Push with dry run."""
- remote_dir = posixpath.join(self.DEVICE_TEMP_DIR, 'contents')
- remote_links = posixpath.join(self.DEVICE_TEMP_DIR, 'links')
- remote_symlink = posixpath.join(self.DEVICE_TEMP_DIR, 'symlink')
+ for file_size in [8, 1024 * 1024]:
+ try:
+ device_dir = posixpath.join(self.DEVICE_TEMP_DIR, 'push_dry_run')
+ device_file = posixpath.join(device_dir, 'file')
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- self.device.shell(['mkdir', '-p', remote_dir, remote_links])
- self.device.shell(['ln', '-s', remote_links, remote_symlink])
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', '-p', device_dir])
+ self.device.shell(['echo', 'foo', '>', device_file])
- # Populate device directory with random files.
- temp_files = make_random_device_files(
- self.device, in_dir=remote_dir, num_files=32)
+ host_dir = tempfile.mkdtemp()
+ host_file = posixpath.join(host_dir, 'file')
- for temp_file in temp_files:
- self.device.shell(
- ['ln', '-s', '../contents/{}'.format(temp_file.base_name),
- posixpath.join(remote_links, temp_file.base_name)])
+ with open(host_file, "w") as f:
+ f.write('x' * file_size)
- self.device.pull(remote=remote_symlink, local=host_dir)
+ self.device._simple_call(['push', '-n', host_file, device_file])
+ stdout, stderr = self.device.shell(['cat', device_file])
+ self.assertEqual(stdout.strip(), "foo")
- for temp_file in temp_files:
- host_path = os.path.join(
- host_dir, 'symlink', temp_file.base_name)
- self._verify_local(temp_file.checksum, host_path)
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- finally:
- if host_dir is not None:
- shutil.rmtree(host_dir)
+ def test_unicode_paths(self):
+ """Ensure that we can support non-ASCII paths, even on Windows."""
+ name = u'로보카 폴리'
- def test_pull_empty(self):
- """Pull a directory containing an empty directory from the device."""
- try:
- host_dir = tempfile.mkdtemp()
+ self.device.shell(['rm', '-f', '/data/local/tmp/adb-test-*'])
+ remote_path = u'/data/local/tmp/adb-test-{}'.format(name)
- remote_empty_path = posixpath.join(self.DEVICE_TEMP_DIR, 'empty')
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- self.device.shell(['mkdir', '-p', remote_empty_path])
+ ## push.
+ tf = tempfile.NamedTemporaryFile('wb', suffix=name, delete=False)
+ tf.close()
+ self.device.push(tf.name, remote_path)
+ os.remove(tf.name)
+ self.assertFalse(os.path.exists(tf.name))
- self.device.pull(remote=remote_empty_path, local=host_dir)
- self.assertTrue(os.path.isdir(os.path.join(host_dir, 'empty')))
- finally:
- if host_dir is not None:
- shutil.rmtree(host_dir)
+ # Verify that the device ended up with the expected UTF-8 path
+ output = self.device.shell(
+ ['ls', '/data/local/tmp/adb-test-*'])[0].strip()
+ self.assertEqual(remote_path, output)
- def test_multiple_pull(self):
- """Pull a randomly generated directory of files from the device."""
+ # pull.
+ self.device.pull(remote_path, tf.name)
+ self.assertTrue(os.path.exists(tf.name))
+ os.remove(tf.name)
+ self.device.shell(['rm', '-f', '/data/local/tmp/adb-test-*'])
- try:
- host_dir = tempfile.mkdtemp()
- subdir = posixpath.join(self.DEVICE_TEMP_DIR, 'subdir')
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- self.device.shell(['mkdir', '-p', subdir])
+class FileOperationsTestUncompressed(FileOperationsTest.Base):
+ compression = "none"
- # Create some random files and a subdirectory containing more files.
- temp_files = make_random_device_files(
- self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=4)
- subdir_temp_files = make_random_device_files(
- self.device, in_dir=subdir, num_files=4, prefix='subdir_')
+class FileOperationsTestBrotli(FileOperationsTest.Base):
+ compression = "brotli"
- paths = [x.full_path for x in temp_files]
- paths.append(subdir)
- self.device._simple_call(['pull'] + paths + [host_dir])
- for temp_file in temp_files:
- local_path = os.path.join(host_dir, temp_file.base_name)
- self._verify_local(temp_file.checksum, local_path)
+class FileOperationsTestLZ4(FileOperationsTest.Base):
+ compression = "lz4"
- for subdir_temp_file in subdir_temp_files:
- local_path = os.path.join(host_dir,
- 'subdir',
- subdir_temp_file.base_name)
- self._verify_local(subdir_temp_file.checksum, local_path)
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- finally:
- if host_dir is not None:
- shutil.rmtree(host_dir)
-
- def verify_sync(self, device, temp_files, device_dir):
- """Verifies that a list of temp files was synced to the device."""
- # Confirm that every file on the device mirrors that on the host.
- for temp_file in temp_files:
- device_full_path = posixpath.join(
- device_dir, temp_file.base_name)
- dev_md5, _ = device.shell(
- [get_md5_prog(self.device), device_full_path])[0].split()
- self.assertEqual(temp_file.checksum, dev_md5)
-
- def test_sync(self):
- """Sync a host directory to the data partition."""
-
- try:
- base_dir = tempfile.mkdtemp()
-
- # Create mirror device directory hierarchy within base_dir.
- full_dir_path = base_dir + self.DEVICE_TEMP_DIR
- os.makedirs(full_dir_path)
-
- # Create 32 random files within the host mirror.
- temp_files = make_random_host_files(
- in_dir=full_dir_path, num_files=32)
-
- # Clean up any stale files on the device.
- device = adb.get_device() # pylint: disable=no-member
- device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
-
- old_product_out = os.environ.get('ANDROID_PRODUCT_OUT')
- os.environ['ANDROID_PRODUCT_OUT'] = base_dir
- device.sync('data')
- if old_product_out is None:
- del os.environ['ANDROID_PRODUCT_OUT']
- else:
- os.environ['ANDROID_PRODUCT_OUT'] = old_product_out
-
- self.verify_sync(device, temp_files, self.DEVICE_TEMP_DIR)
-
- #self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- finally:
- if base_dir is not None:
- shutil.rmtree(base_dir)
-
- def test_push_sync(self):
- """Sync a host directory to a specific path."""
-
- try:
- temp_dir = tempfile.mkdtemp()
- temp_files = make_random_host_files(in_dir=temp_dir, num_files=32)
-
- device_dir = posixpath.join(self.DEVICE_TEMP_DIR, 'sync_src_dst')
-
- # Clean up any stale files on the device.
- device = adb.get_device() # pylint: disable=no-member
- device.shell(['rm', '-rf', device_dir])
-
- device.push(temp_dir, device_dir, sync=True)
-
- self.verify_sync(device, temp_files, device_dir)
-
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- finally:
- if temp_dir is not None:
- shutil.rmtree(temp_dir)
-
- def test_unicode_paths(self):
- """Ensure that we can support non-ASCII paths, even on Windows."""
- name = u'로보카 폴리'
-
- self.device.shell(['rm', '-f', '/data/local/tmp/adb-test-*'])
- remote_path = u'/data/local/tmp/adb-test-{}'.format(name)
-
- ## push.
- tf = tempfile.NamedTemporaryFile('wb', suffix=name, delete=False)
- tf.close()
- self.device.push(tf.name, remote_path)
- os.remove(tf.name)
- self.assertFalse(os.path.exists(tf.name))
-
- # Verify that the device ended up with the expected UTF-8 path
- output = self.device.shell(
- ['ls', '/data/local/tmp/adb-test-*'])[0].strip()
- self.assertEqual(remote_path, output)
-
- # pull.
- self.device.pull(remote_path, tf.name)
- self.assertTrue(os.path.exists(tf.name))
- os.remove(tf.name)
- self.device.shell(['rm', '-f', '/data/local/tmp/adb-test-*'])
+class FileOperationsTestZstd(FileOperationsTest.Base):
+ compression = "zstd"
class DeviceOfflineTest(DeviceTest):
diff --git a/adb/tls/Android.bp b/adb/tls/Android.bp
index f2837e1..e5204f3 100644
--- a/adb/tls/Android.bp
+++ b/adb/tls/Android.bp
@@ -39,6 +39,7 @@
recovery_available: true,
visibility: [
+ "//bootable/recovery/minadbd:__subpackages__",
"//system/core/adb:__subpackages__",
],
diff --git a/adb/tools/Android.bp b/adb/tools/Android.bp
index 71e32b7..a7af53c 100644
--- a/adb/tools/Android.bp
+++ b/adb/tools/Android.bp
@@ -34,3 +34,26 @@
],
},
}
+
+cc_binary_host {
+ name: "adb_usbreset",
+
+ defaults: ["adb_defaults"],
+
+ srcs: [
+ "adb_usbreset.cpp",
+ ],
+
+ static_libs: [
+ "libbase",
+ "libusb",
+ ],
+
+ stl: "libc++_static",
+
+ dist: {
+ targets: [
+ "sdk",
+ ],
+ },
+}
diff --git a/adb/tools/adb_usbreset.cpp b/adb/tools/adb_usbreset.cpp
new file mode 100644
index 0000000..6f141bd
--- /dev/null
+++ b/adb/tools/adb_usbreset.cpp
@@ -0,0 +1,188 @@
+// 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 <err.h>
+#include <getopt.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <string>
+#include <string_view>
+#include <variant>
+#include <vector>
+
+#include <libusb/libusb.h>
+
+struct AllDevices {};
+struct SingleDevice {};
+struct Serial {
+ std::string_view serial;
+};
+
+using DeviceSelection = std::variant<std::monostate, AllDevices, SingleDevice, Serial>;
+
+[[noreturn]] static void Usage(int rc) {
+ fprintf(stderr, "usage: [ANDROID_SERIAL=SERIAL] usbreset [-d] [-s SERIAL]\n");
+ fprintf(stderr, "\t-a --all\t\tReset all connected devices\n");
+ fprintf(stderr, "\t-d --device\t\tReset the single connected device\n");
+ fprintf(stderr, "\t-s --serial\t\tReset device with specified serial\n");
+ exit(rc);
+}
+
+static void SetOption(DeviceSelection* out, DeviceSelection in) {
+ if (!std::get_if<std::monostate>(out)) {
+ printf("error: multiple device selection options provided\n");
+ Usage(1);
+ }
+
+ *out = in;
+}
+
+static __attribute__((format(printf, 2, 3))) void PrintLibusbError(int err, const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ vprintf(fmt, args);
+ vprintf(fmt, args);
+ va_end(args);
+
+ printf(": %s", libusb_strerror(static_cast<libusb_error>(err)));
+}
+
+static bool IsAdbInterface(const libusb_interface_descriptor* desc) {
+ return desc->bInterfaceClass == 0xFF && desc->bInterfaceSubClass == 0x42 &&
+ desc->bInterfaceProtocol == 0x1;
+}
+
+int main(int argc, char** argv) {
+ std::variant<std::monostate, AllDevices, SingleDevice, Serial> selection;
+
+ static constexpr struct option long_opts[] = {
+ {"all", 0, 0, 'a'}, {"help", 0, 0, 'h'}, {"serial", required_argument, 0, 's'},
+ {"device", 0, 0, 'd'}, {0, 0, 0, 0},
+ };
+
+ int opt;
+ while ((opt = getopt_long(argc, argv, "adhs:", long_opts, nullptr)) != -1) {
+ if (opt == 'h') {
+ Usage(0);
+ } else if (opt == 'a') {
+ SetOption(&selection, AllDevices{});
+ } else if (opt == 's') {
+ SetOption(&selection, Serial{optarg});
+ } else if (opt == 'd') {
+ SetOption(&selection, Serial{optarg});
+ } else {
+ errx(1, "unknown option: '%c'", opt);
+ }
+ }
+
+ if (std::get_if<std::monostate>(&selection)) {
+ const char* env = getenv("ANDROID_SERIAL");
+ if (env) {
+ SetOption(&selection, Serial{env});
+ } else {
+ fprintf(stderr, "adb_usbreset: no device specified\n");
+ Usage(1);
+ }
+ }
+
+ libusb_context* ctx;
+ int rc = libusb_init(&ctx);
+ if (rc != LIBUSB_SUCCESS) {
+ PrintLibusbError(rc, "error: failed to initialize libusb");
+ exit(1);
+ }
+
+ libusb_device** device_list;
+ ssize_t device_count = libusb_get_device_list(ctx, &device_list);
+ if (device_count < 0) {
+ PrintLibusbError(device_count, "error: failed to list devices");
+ exit(1);
+ }
+
+ std::vector<std::pair<std::string, libusb_device_handle*>> selected_devices;
+ for (int i = 0; i < device_count; ++i) {
+ libusb_device* device = device_list[i];
+ libusb_device_descriptor device_desc;
+
+ // Always succeeds for LIBUSB_API_VERSION >= 0x01000102.
+ libusb_get_device_descriptor(device, &device_desc);
+ static_assert(LIBUSB_API_VERSION >= 0x01000102);
+
+ libusb_config_descriptor* config_desc;
+ rc = libusb_get_active_config_descriptor(device, &config_desc);
+ if (rc != 0) {
+ PrintLibusbError(rc, "warning: failed to get config descriptor");
+ continue;
+ }
+
+ bool found_adb_interface = false;
+ for (int i = 0; i < config_desc->bNumInterfaces; ++i) {
+ if (IsAdbInterface(&config_desc->interface[i].altsetting[0])) {
+ found_adb_interface = true;
+ break;
+ }
+ }
+
+ if (found_adb_interface) {
+ libusb_device_handle* device_handle;
+ rc = libusb_open(device, &device_handle);
+ if (rc != 0) {
+ PrintLibusbError(rc, "warning: failed to open device");
+ continue;
+ }
+
+ char buf[128];
+ rc = libusb_get_string_descriptor_ascii(device_handle, device_desc.iSerialNumber,
+ reinterpret_cast<unsigned char*>(buf),
+ sizeof(buf));
+
+ if (rc < 0) {
+ PrintLibusbError(rc, "warning: failed to get device serial");
+ continue;
+ }
+
+ std::string serial(buf, buf + rc);
+ if (auto s = std::get_if<Serial>(&selection)) {
+ if (s->serial == serial) {
+ selected_devices.push_back(std::make_pair(std::move(serial), device_handle));
+ }
+ } else {
+ selected_devices.push_back(std::make_pair(std::move(serial), device_handle));
+ }
+ }
+ }
+
+ if (selected_devices.empty()) {
+ errx(1, "no devices match criteria");
+ } else if (std::get_if<SingleDevice>(&selection) && selected_devices.size() != 1) {
+ errx(1, "more than 1 device connected");
+ }
+
+ bool success = true;
+ for (auto& [serial, device_handle] : selected_devices) {
+ rc = libusb_reset_device(device_handle);
+ // libusb_reset_device will try to restore the previous state, and will return
+ // LIBUSB_ERROR_NOT_FOUND if it can't.
+ if (rc == 0 || rc == LIBUSB_ERROR_NOT_FOUND) {
+ printf("%s: successfully reset\n", serial.c_str());
+ } else {
+ PrintLibusbError(rc, "%s: failed to reset", serial.c_str());
+ success = false;
+ }
+ }
+
+ return !success;
+}
diff --git a/adb/tools/check_ms_os_desc.cpp b/adb/tools/check_ms_os_desc.cpp
index 8743ff7..8e85809 100644
--- a/adb/tools/check_ms_os_desc.cpp
+++ b/adb/tools/check_ms_os_desc.cpp
@@ -131,8 +131,6 @@
errx(1, "failed to retrieve MS OS v1 compat descriptor: %s", libusb_error_name(rc));
}
- memcpy(&hdr, data.data(), data.size());
-
struct __attribute__((packed)) ms_os_desc_v1_function {
uint8_t bFirstInterfaceNumber;
uint8_t reserved1;
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 8b3461a..b6b6984 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -29,7 +29,6 @@
#include <unistd.h>
#include <algorithm>
-#include <deque>
#include <list>
#include <memory>
#include <mutex>
@@ -40,6 +39,7 @@
#include <adb/crypto/x509_generator.h>
#include <adb/tls/tls_connection.h>
#include <android-base/logging.h>
+#include <android-base/no_destructor.h>
#include <android-base/parsenetaddress.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
@@ -81,6 +81,12 @@
const char* const kFeatureFixedPushSymlinkTimestamp = "fixed_push_symlink_timestamp";
const char* const kFeatureAbbExec = "abb_exec";
const char* const kFeatureRemountShell = "remount_shell";
+const char* const kFeatureTrackApp = "track_app";
+const char* const kFeatureSendRecv2 = "sendrecv_v2";
+const char* const kFeatureSendRecv2Brotli = "sendrecv_v2_brotli";
+const char* const kFeatureSendRecv2LZ4 = "sendrecv_v2_lz4";
+const char* const kFeatureSendRecv2Zstd = "sendrecv_v2_zstd";
+const char* const kFeatureSendRecv2DryRunSend = "sendrecv_v2_dry_run_send";
namespace {
@@ -497,12 +503,13 @@
auto x509 = GenerateX509Certificate(evp_pkey.get());
auto x509_str = X509ToPEMString(x509.get());
auto evp_str = Key::ToPEMString(evp_pkey.get());
+
+ int osh = cast_handle_to_int(adb_get_os_handle(fd_));
#if ADB_HOST
- tls_ = TlsConnection::Create(TlsConnection::Role::Client,
+ tls_ = TlsConnection::Create(TlsConnection::Role::Client, x509_str, evp_str, osh);
#else
- tls_ = TlsConnection::Create(TlsConnection::Role::Server,
+ tls_ = TlsConnection::Create(TlsConnection::Role::Server, x509_str, evp_str, osh);
#endif
- x509_str, evp_str, fd_);
CHECK(tls_);
#if ADB_HOST
// TLS 1.3 gives the client no message if the server rejected the
@@ -922,6 +929,7 @@
remove_transport(t);
}
+#if ADB_HOST
static int qual_match(const std::string& to_test, const char* prefix, const std::string& qual,
bool sanitize_qual) {
if (to_test.empty()) /* Return true if both the qual and to_test are empty strings. */
@@ -1077,10 +1085,13 @@
}
cv_.notify_one();
}
+#endif
atransport::~atransport() {
+#if ADB_HOST
// If the connection callback had not been run before, run it now.
SetConnectionEstablished(false);
+#endif
}
int atransport::Write(apacket* p) {
@@ -1163,23 +1174,30 @@
}
const FeatureSet& supported_features() {
- // Local static allocation to avoid global non-POD variables.
- static const FeatureSet* features = new FeatureSet{
- kFeatureShell2,
- kFeatureCmd,
- kFeatureStat2,
- kFeatureLs2,
- kFeatureFixedPushMkdir,
- kFeatureApex,
- kFeatureAbb,
- kFeatureFixedPushSymlinkTimestamp,
- kFeatureAbbExec,
- kFeatureRemountShell,
- // Increment ADB_SERVER_VERSION when adding a feature that adbd needs
- // to know about. Otherwise, the client can be stuck running an old
- // version of the server even after upgrading their copy of adb.
- // (http://b/24370690)
- };
+ static const android::base::NoDestructor<FeatureSet> features([] {
+ return FeatureSet{
+ kFeatureShell2,
+ kFeatureCmd,
+ kFeatureStat2,
+ kFeatureLs2,
+ kFeatureFixedPushMkdir,
+ kFeatureApex,
+ kFeatureAbb,
+ kFeatureFixedPushSymlinkTimestamp,
+ kFeatureAbbExec,
+ kFeatureRemountShell,
+ kFeatureTrackApp,
+ kFeatureSendRecv2,
+ kFeatureSendRecv2Brotli,
+ kFeatureSendRecv2LZ4,
+ kFeatureSendRecv2Zstd,
+ kFeatureSendRecv2DryRunSend,
+ // Increment ADB_SERVER_VERSION when adding a feature that adbd needs
+ // to know about. Otherwise, the client can be stuck running an old
+ // version of the server even after upgrading their copy of adb.
+ // (http://b/24370690)
+ };
+ }());
return *features;
}
@@ -1193,16 +1211,20 @@
return FeatureSet();
}
- auto names = android::base::Split(features_string, ",");
- return FeatureSet(names.begin(), names.end());
+ return android::base::Split(features_string, ",");
+}
+
+template <class Range, class Value>
+static bool contains(const Range& r, const Value& v) {
+ return std::find(std::begin(r), std::end(r), v) != std::end(r);
}
bool CanUseFeature(const FeatureSet& feature_set, const std::string& feature) {
- return feature_set.count(feature) > 0 && supported_features().count(feature) > 0;
+ return contains(feature_set, feature) && contains(supported_features(), feature);
}
bool atransport::has_feature(const std::string& feature) const {
- return features_.count(feature) > 0;
+ return contains(features_, feature);
}
void atransport::SetFeatures(const std::string& features_string) {
@@ -1224,6 +1246,7 @@
disconnects_.clear();
}
+#if ADB_HOST
bool atransport::MatchesTarget(const std::string& target) const {
if (!serial.empty()) {
if (target == serial) {
@@ -1267,8 +1290,6 @@
return reconnect_(this);
}
-#if ADB_HOST
-
// We use newline as our delimiter, make sure to never output it.
static std::string sanitize(std::string str, bool alphanumeric) {
auto pred = alphanumeric ? [](const char c) { return !isalnum(c); }
@@ -1350,7 +1371,7 @@
void close_usb_devices(bool reset) {
close_usb_devices([](const atransport*) { return true; }, reset);
}
-#endif // ADB_HOST
+#endif
bool register_socket_transport(unique_fd s, std::string serial, int port, int local,
atransport::ReconnectCallback reconnect, bool use_tls, int* error) {
@@ -1390,7 +1411,9 @@
lock.unlock();
+#if ADB_HOST
auto waitable = t->connection_waitable();
+#endif
register_transport(t);
if (local == 1) {
@@ -1398,6 +1421,7 @@
return true;
}
+#if ADB_HOST
if (!waitable->WaitForConnection(std::chrono::seconds(10))) {
if (error) *error = ETIMEDOUT;
return false;
@@ -1407,6 +1431,7 @@
if (error) *error = EPERM;
return false;
}
+#endif
return true;
}
@@ -1437,13 +1462,9 @@
t->Kick();
}
}
-#if ADB_HOST
reconnect_handler.CheckForKicked();
-#endif
}
-#endif
-
void register_usb_transport(usb_handle* usb, const char* serial, const char* devpath,
unsigned writeable) {
atransport* t = new atransport(writeable ? kCsOffline : kCsNoPerm);
@@ -1466,7 +1487,6 @@
register_transport(t);
}
-#if ADB_HOST
// This should only be used for transports with connection_state == kCsNoPerm.
void unregister_usb_transport(usb_handle* usb) {
std::lock_guard<std::recursive_mutex> lock(transport_lock);
diff --git a/adb/transport.h b/adb/transport.h
index 8a0f62a..b1f2744 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -30,7 +30,7 @@
#include <string>
#include <string_view>
#include <thread>
-#include <unordered_set>
+#include <vector>
#include <android-base/macros.h>
#include <android-base/thread_annotations.h>
@@ -39,9 +39,11 @@
#include "adb.h"
#include "adb_unique_fd.h"
#include "types.h"
-#include "usb.h"
-typedef std::unordered_set<std::string> FeatureSet;
+// Even though the feature set is used as a set, we only have a dozen or two
+// of available features at any moment. Vector works much better in terms of
+// both memory usage and performance for these sizes.
+using FeatureSet = std::vector<std::string>;
namespace adb {
namespace tls {
@@ -81,7 +83,20 @@
extern const char* const kFeatureAbbExec;
// adbd properly updates symlink timestamps on push.
extern const char* const kFeatureFixedPushSymlinkTimestamp;
+// Implement `adb remount` via shelling out to /system/bin/remount.
extern const char* const kFeatureRemountShell;
+// adbd supports `track-app` service reporting debuggable/profileable apps.
+extern const char* const kFeatureTrackApp;
+// adbd supports version 2 of send/recv.
+extern const char* const kFeatureSendRecv2;
+// adbd supports brotli for send/recv v2.
+extern const char* const kFeatureSendRecv2Brotli;
+// adbd supports LZ4 for send/recv v2.
+extern const char* const kFeatureSendRecv2LZ4;
+// adbd supports Zstd for send/recv v2.
+extern const char* const kFeatureSendRecv2Zstd;
+// adbd supports dry-run send for send/recv v2.
+extern const char* const kFeatureSendRecv2DryRunSend;
TransportId NextTransportId();
@@ -197,20 +212,6 @@
std::unique_ptr<adb::tls::TlsConnection> tls_;
};
-struct UsbConnection : public BlockingConnection {
- explicit UsbConnection(usb_handle* handle) : handle_(handle) {}
- ~UsbConnection();
-
- bool Read(apacket* packet) override final;
- bool Write(apacket* packet) override final;
- bool DoTlsHandshake(RSA* key, std::string* auth_key) override final;
-
- void Close() override final;
- virtual void Reset() override final;
-
- usb_handle* handle_;
-};
-
// Waits for a transport's connection to be not pending. This is a separate
// object so that the transport can be destroyed and another thread can be
// notified of it in a race-free way.
@@ -246,6 +247,10 @@
Abort,
};
+#if ADB_HOST
+struct usb_handle;
+#endif
+
class atransport : public enable_weak_from_this<atransport> {
public:
// TODO(danalbert): We expose waaaaaaay too much stuff because this was
@@ -259,9 +264,12 @@
: id(NextTransportId()),
kicked_(false),
connection_state_(state),
- connection_waitable_(std::make_shared<ConnectionWaitable>()),
connection_(nullptr),
reconnect_(std::move(reconnect)) {
+#if ADB_HOST
+ connection_waitable_ = std::make_shared<ConnectionWaitable>();
+#endif
+
// Initialize protocol to min version for compatibility with older versions.
// Version will be updated post-connect.
protocol_version = A_VERSION_MIN;
@@ -286,8 +294,10 @@
return connection_;
}
+#if ADB_HOST
void SetUsbHandle(usb_handle* h) { usb_handle_ = h; }
usb_handle* GetUsbHandle() { return usb_handle_; }
+#endif
const TransportId id;
@@ -345,6 +355,7 @@
void RemoveDisconnect(adisconnect* disconnect);
void RunDisconnects();
+#if ADB_HOST
// Returns true if |target| matches this transport. A matching |target| can be any of:
// * <serial>
// * <devpath>
@@ -369,6 +380,7 @@
// Attempts to reconnect with the underlying Connection.
ReconnectResult Reconnect();
+#endif
private:
std::atomic<bool> kicked_;
@@ -387,15 +399,19 @@
std::deque<std::shared_ptr<RSA>> keys_;
#endif
+#if ADB_HOST
// A sharable object that can be used to wait for the atransport's
// connection to be established.
std::shared_ptr<ConnectionWaitable> connection_waitable_;
+#endif
// The underlying connection object.
std::shared_ptr<Connection> connection_ GUARDED_BY(mutex_);
+#if ADB_HOST
// USB handle for the connection, if available.
usb_handle* usb_handle_ = nullptr;
+#endif
// A callback that will be invoked when the atransport needs to reconnect.
ReconnectCallback reconnect_;
@@ -427,17 +443,31 @@
void init_transport_registration(void);
void init_mdns_transport_discovery(void);
std::string list_transports(bool long_listing);
+
+#if ADB_HOST
atransport* find_transport(const char* serial);
+
void kick_all_tcp_devices();
+#endif
+
void kick_all_transports();
+
void kick_all_tcp_tls_transports();
+
#if !ADB_HOST
void kick_all_transports_by_auth_key(std::string_view auth_key);
#endif
void register_transport(atransport* transport);
-void register_usb_transport(usb_handle* h, const char* serial,
- const char* devpath, unsigned writeable);
+
+#if ADB_HOST
+void init_usb_transport(atransport* t, usb_handle* usb);
+void register_usb_transport(usb_handle* h, const char* serial, const char* devpath,
+ unsigned writeable);
+
+// This should only be used for transports with connection_state == kCsNoPerm.
+void unregister_usb_transport(usb_handle* usb);
+#endif
/* Connect to a network address and register it as a device */
void connect_device(const std::string& address, std::string* response);
@@ -447,9 +477,6 @@
atransport::ReconnectCallback reconnect, bool use_tls,
int* error = nullptr);
-// This should only be used for transports with connection_state == kCsNoPerm.
-void unregister_usb_transport(usb_handle* usb);
-
bool check_header(apacket* p, atransport* t);
void close_usb_devices(bool reset = false);
diff --git a/adb/transport_test.cpp b/adb/transport_test.cpp
index 00beb3a..8579ff4 100644
--- a/adb/transport_test.cpp
+++ b/adb/transport_test.cpp
@@ -66,7 +66,7 @@
ASSERT_TRUE(t.has_feature("bar"));
t.SetFeatures(FeatureSetToString(FeatureSet{"foo", "bar", "foo"}));
- ASSERT_EQ(2U, t.features().size());
+ ASSERT_LE(2U, t.features().size());
ASSERT_TRUE(t.has_feature("foo"));
ASSERT_TRUE(t.has_feature("bar"));
@@ -127,6 +127,7 @@
ASSERT_EQ(std::string("baz"), t.device);
}
+#if ADB_HOST
TEST_F(TransportTest, test_matches_target) {
std::string serial = "foo";
std::string devpath = "/path/to/bar";
@@ -183,3 +184,4 @@
EXPECT_FALSE(t.MatchesTarget("abc:100.100.100.100"));
}
}
+#endif
diff --git a/adb/types.cpp b/adb/types.cpp
index 26b77ab..9cdf32b 100644
--- a/adb/types.cpp
+++ b/adb/types.cpp
@@ -51,7 +51,7 @@
auto dropped = 0u;
while (dropped < len) {
const auto next = chain_[start_index_].size() - begin_offset_;
- if (dropped + next < len) {
+ if (dropped + next <= len) {
pop_front_block();
dropped += next;
} else {
diff --git a/adb/types.h b/adb/types.h
index c619fff..620aa8e 100644
--- a/adb/types.h
+++ b/adb/types.h
@@ -150,6 +150,22 @@
IOVector& operator=(const IOVector& copy) = delete;
IOVector& operator=(IOVector&& move) noexcept;
+ const value_type* front_data() const {
+ if (chain_.empty()) {
+ return nullptr;
+ }
+
+ return chain_[start_index_].data() + begin_offset_;
+ }
+
+ size_type front_size() const {
+ if (chain_.empty()) {
+ return 0;
+ }
+
+ return chain_[start_index_].size() - begin_offset_;
+ }
+
size_type size() const { return chain_length_ - begin_offset_; }
bool empty() const { return size() == 0; }
diff --git a/adb/types_test.cpp b/adb/types_test.cpp
index 2c99f95..41fa1db 100644
--- a/adb/types_test.cpp
+++ b/adb/types_test.cpp
@@ -117,3 +117,20 @@
ASSERT_EQ(1ULL, bc.size());
ASSERT_EQ(create_block("x"), bc.coalesce());
}
+
+TEST(IOVector, drop_front) {
+ IOVector vec;
+
+ vec.append(create_block('x', 2));
+ vec.append(create_block('y', 1000));
+ ASSERT_EQ(2U, vec.front_size());
+ ASSERT_EQ(1002U, vec.size());
+
+ vec.drop_front(1);
+ ASSERT_EQ(1U, vec.front_size());
+ ASSERT_EQ(1001U, vec.size());
+
+ vec.drop_front(1);
+ ASSERT_EQ(1000U, vec.front_size());
+ ASSERT_EQ(1000U, vec.size());
+}
diff --git a/base b/base
new file mode 120000
index 0000000..622c552
--- /dev/null
+++ b/base
@@ -0,0 +1 @@
+../libbase
\ No newline at end of file
diff --git a/base/.clang-format b/base/.clang-format
deleted file mode 120000
index fd0645f..0000000
--- a/base/.clang-format
+++ /dev/null
@@ -1 +0,0 @@
-../.clang-format-2
\ No newline at end of file
diff --git a/base/Android.bp b/base/Android.bp
deleted file mode 100644
index 25c74f2..0000000
--- a/base/Android.bp
+++ /dev/null
@@ -1,222 +0,0 @@
-//
-// Copyright (C) 2015 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-cc_defaults {
- name: "libbase_cflags_defaults",
- cflags: [
- "-Wall",
- "-Werror",
- "-Wextra",
- ],
- target: {
- android: {
- cflags: [
- "-D_FILE_OFFSET_BITS=64",
- ],
- },
- },
-}
-
-cc_library_headers {
- name: "libbase_headers",
- vendor_available: true,
- ramdisk_available: true,
- recovery_available: true,
- host_supported: true,
- native_bridge_supported: true,
- export_include_dirs: ["include"],
-
- target: {
- linux_bionic: {
- enabled: true,
- },
- windows: {
- enabled: true,
- },
- },
-}
-
-cc_defaults {
- name: "libbase_defaults",
- defaults: ["libbase_cflags_defaults"],
- srcs: [
- "abi_compatibility.cpp",
- "chrono_utils.cpp",
- "cmsg.cpp",
- "file.cpp",
- "liblog_symbols.cpp",
- "logging.cpp",
- "mapped_file.cpp",
- "parsebool.cpp",
- "parsenetaddress.cpp",
- "process.cpp",
- "properties.cpp",
- "stringprintf.cpp",
- "strings.cpp",
- "threads.cpp",
- "test_utils.cpp",
- ],
-
- static: {
- cflags: ["-DNO_LIBLOG_DLSYM"],
- },
-
- cppflags: ["-Wexit-time-destructors"],
- shared_libs: ["liblog"],
- target: {
- android: {
- sanitize: {
- misc_undefined: ["integer"],
- },
-
- },
- linux: {
- srcs: [
- "errors_unix.cpp",
- ],
- },
- darwin: {
- srcs: [
- "errors_unix.cpp",
- ],
- },
- linux_bionic: {
- enabled: true,
- },
- windows: {
- srcs: [
- "errors_windows.cpp",
- "utf8.cpp",
- ],
- exclude_srcs: [
- "cmsg.cpp",
- ],
- enabled: true,
- },
- },
-}
-
-cc_library {
- name: "libbase",
- defaults: ["libbase_defaults"],
- vendor_available: true,
- ramdisk_available: true,
- recovery_available: true,
- host_supported: true,
- native_bridge_supported: true,
- vndk: {
- enabled: true,
- support_system_process: true,
- },
- header_libs: [
- "libbase_headers",
- ],
- export_header_lib_headers: ["libbase_headers"],
- static_libs: ["fmtlib"],
- whole_static_libs: ["fmtlib"],
- export_static_lib_headers: ["fmtlib"],
- apex_available: [
- "//apex_available:anyapex",
- "//apex_available:platform",
- ],
-}
-
-cc_library_static {
- name: "libbase_ndk",
- defaults: ["libbase_defaults"],
- sdk_version: "current",
- stl: "c++_static",
- export_include_dirs: ["include"],
- static_libs: ["fmtlib_ndk"],
- whole_static_libs: ["fmtlib_ndk"],
- export_static_lib_headers: ["fmtlib_ndk"],
-}
-
-// Tests
-// ------------------------------------------------------------------------------
-cc_test {
- name: "libbase_test",
- defaults: ["libbase_cflags_defaults"],
- host_supported: true,
- srcs: [
- "cmsg_test.cpp",
- "endian_test.cpp",
- "errors_test.cpp",
- "expected_test.cpp",
- "file_test.cpp",
- "logging_test.cpp",
- "macros_test.cpp",
- "mapped_file_test.cpp",
- "no_destructor_test.cpp",
- "parsedouble_test.cpp",
- "parsebool_test.cpp",
- "parseint_test.cpp",
- "parsenetaddress_test.cpp",
- "process_test.cpp",
- "properties_test.cpp",
- "result_test.cpp",
- "scopeguard_test.cpp",
- "stringprintf_test.cpp",
- "strings_test.cpp",
- "test_main.cpp",
- "test_utils_test.cpp",
- ],
- target: {
- android: {
- sanitize: {
- misc_undefined: ["integer"],
- },
- },
- linux: {
- srcs: ["chrono_utils_test.cpp"],
- },
- windows: {
- srcs: ["utf8_test.cpp"],
- cflags: ["-Wno-unused-parameter"],
- enabled: true,
- },
- },
- local_include_dirs: ["."],
- shared_libs: ["libbase"],
- compile_multilib: "both",
- multilib: {
- lib32: {
- suffix: "32",
- },
- lib64: {
- suffix: "64",
- },
- },
- test_suites: ["device-tests"],
-}
-
-cc_benchmark {
- name: "libbase_benchmark",
- defaults: ["libbase_cflags_defaults"],
-
- srcs: ["format_benchmark.cpp"],
- shared_libs: ["libbase"],
-
- compile_multilib: "both",
- multilib: {
- lib32: {
- suffix: "32",
- },
- lib64: {
- suffix: "64",
- },
- },
-}
diff --git a/base/CPPLINT.cfg b/base/CPPLINT.cfg
deleted file mode 100644
index d94a89c..0000000
--- a/base/CPPLINT.cfg
+++ /dev/null
@@ -1,2 +0,0 @@
-set noparent
-filter=-build/header_guard,-build/include,-build/c++11,-whitespace/operators
diff --git a/base/OWNERS b/base/OWNERS
deleted file mode 100644
index 97777f7..0000000
--- a/base/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-enh@google.com
-jmgao@google.com
-tomcherry@google.com
diff --git a/base/README.md b/base/README.md
deleted file mode 100644
index 2ef5c10..0000000
--- a/base/README.md
+++ /dev/null
@@ -1,42 +0,0 @@
-# libbase
-
-## Who is this library for?
-
-This library is a collection of convenience functions to make common tasks
-easier and less error-prone.
-
-In this context, "error-prone" covers both "hard to do correctly" and
-"hard to do with good performance", but as a general purpose library,
-libbase's primary focus is on making it easier to do things easily and
-correctly when a compromise has to be made between "simplest API" on the
-one hand and "fastest implementation" on the other. Though obviously
-the ideal is to have both.
-
-## Should my routine be added?
-
-The intention is to cover the 80% use cases, not be all things to all users.
-
-If you have a routine that's really useful in your project,
-congratulations. But that doesn't mean it should be here rather than
-just in your project.
-
-The question for libbase is "should everyone be doing this?"/"does this
-make everyone's code cleaner/safer?". Historically we've considered the
-bar for inclusion to be "are there at least three *unrelated* projects
-that would be cleaned up by doing so".
-
-If your routine is actually something from a future C++ standard (that
-isn't yet in libc++), or it's widely used in another library, that helps
-show that there's precedent. Being able to say "so-and-so has used this
-API for n years" is a good way to reduce concerns about API choices.
-
-## Any other restrictions?
-
-Unlike most Android code, code in libbase has to build for Mac and
-Windows too.
-
-Code here is also expected to have good test coverage.
-
-By its nature, it's difficult to change libbase API. It's often best
-to start using your routine just in your project, and let it "graduate"
-after you're certain that the API is solid.
diff --git a/base/abi_compatibility.cpp b/base/abi_compatibility.cpp
deleted file mode 100644
index 06a7801..0000000
--- a/base/abi_compatibility.cpp
+++ /dev/null
@@ -1,77 +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 <memory>
-
-#include "android-base/cmsg.h"
-#include "android-base/file.h"
-#include "android-base/mapped_file.h"
-#include "android-base/unique_fd.h"
-
-namespace android {
-namespace base {
-
-// These ABI-compatibility shims are in a separate file for two reasons:
-// 1. If they were in the file with the actual functions, it prevents calls to
-// those functions by other functions in the file, due to ambiguity.
-// 2. We will hopefully be able to delete these quickly.
-
-#if !defined(_WIN32)
-ssize_t SendFileDescriptorVector(int sockfd, const void* data, size_t len,
- const std::vector<int>& fds) {
- return SendFileDescriptorVector(borrowed_fd(sockfd), data, len, fds);
-}
-
-ssize_t ReceiveFileDescriptorVector(int sockfd, void* data, size_t len, size_t max_fds,
- std::vector<unique_fd>* fds) {
- return ReceiveFileDescriptorVector(borrowed_fd(sockfd), data, len, max_fds, fds);
-}
-#endif
-
-bool ReadFdToString(int fd, std::string* content) {
- return ReadFdToString(borrowed_fd(fd), content);
-}
-
-bool WriteStringToFd(const std::string& content, int fd) {
- return WriteStringToFd(content, borrowed_fd(fd));
-}
-
-bool ReadFully(int fd, void* data, size_t byte_count) {
- return ReadFully(borrowed_fd(fd), data, byte_count);
-}
-
-bool ReadFullyAtOffset(int fd, void* data, size_t byte_count, off64_t offset) {
- return ReadFullyAtOffset(borrowed_fd(fd), data, byte_count, offset);
-}
-
-bool WriteFully(int fd, const void* data, size_t byte_count) {
- return WriteFully(borrowed_fd(fd), data, byte_count);
-}
-
-#if defined(__LP64__)
-#define MAPPEDFILE_FROMFD _ZN10MappedFile6FromFdEilmi
-#else
-#define MAPPEDFILE_FROMFD _ZN10MappedFile6FromFdEixmi
-#endif
-
-#pragma clang diagnostic ignored "-Wreturn-type-c-linkage"
-extern "C" std::unique_ptr<MappedFile> MAPPEDFILE_FROMFD(int fd, off64_t offset, size_t length,
- int prot) {
- return MappedFile::FromFd(fd, offset, length, prot);
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/chrono_utils.cpp b/base/chrono_utils.cpp
deleted file mode 100644
index 19080a5..0000000
--- a/base/chrono_utils.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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.
- */
-
-#include "android-base/chrono_utils.h"
-
-#include <time.h>
-
-namespace android {
-namespace base {
-
-boot_clock::time_point boot_clock::now() {
-#ifdef __linux__
- timespec ts;
- clock_gettime(CLOCK_BOOTTIME, &ts);
- return boot_clock::time_point(std::chrono::seconds(ts.tv_sec) +
- std::chrono::nanoseconds(ts.tv_nsec));
-#else
- // Darwin and Windows do not support clock_gettime.
- return boot_clock::time_point();
-#endif // __linux__
-}
-
-std::ostream& operator<<(std::ostream& os, const Timer& t) {
- os << t.duration().count() << "ms";
- return os;
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/chrono_utils_test.cpp b/base/chrono_utils_test.cpp
deleted file mode 100644
index da442f4..0000000
--- a/base/chrono_utils_test.cpp
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * 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.
- */
-
-#include "android-base/chrono_utils.h"
-
-#include <time.h>
-
-#include <chrono>
-#include <sstream>
-#include <string>
-#include <thread>
-
-#include <gtest/gtest.h>
-
-namespace android {
-namespace base {
-
-std::chrono::seconds GetBootTimeSeconds() {
- struct timespec now;
- clock_gettime(CLOCK_BOOTTIME, &now);
-
- auto now_tp = boot_clock::time_point(std::chrono::seconds(now.tv_sec) +
- std::chrono::nanoseconds(now.tv_nsec));
- return std::chrono::duration_cast<std::chrono::seconds>(now_tp.time_since_epoch());
-}
-
-// Tests (at least) the seconds accuracy of the boot_clock::now() method.
-TEST(ChronoUtilsTest, BootClockNowSeconds) {
- auto now = GetBootTimeSeconds();
- auto boot_seconds =
- std::chrono::duration_cast<std::chrono::seconds>(boot_clock::now().time_since_epoch());
- EXPECT_EQ(now, boot_seconds);
-}
-
-template <typename T>
-void ExpectAboutEqual(T expected, T actual) {
- auto expected_upper_bound = expected * 1.05f;
- auto expected_lower_bound = expected * .95;
- EXPECT_GT(expected_upper_bound, actual);
- EXPECT_LT(expected_lower_bound, actual);
-}
-
-TEST(ChronoUtilsTest, TimerDurationIsSane) {
- auto start = boot_clock::now();
- Timer t;
- std::this_thread::sleep_for(50ms);
- auto stop = boot_clock::now();
- auto stop_timer = t.duration();
-
- auto expected = std::chrono::duration_cast<std::chrono::milliseconds>(stop - start);
- ExpectAboutEqual(expected, stop_timer);
-}
-
-TEST(ChronoUtilsTest, TimerOstream) {
- Timer t;
- std::this_thread::sleep_for(50ms);
- auto stop_timer = t.duration().count();
- std::stringstream os;
- os << t;
- decltype(stop_timer) stop_timer_from_stream;
- os >> stop_timer_from_stream;
- EXPECT_NE(0, stop_timer);
- ExpectAboutEqual(stop_timer, stop_timer_from_stream);
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/cmsg.cpp b/base/cmsg.cpp
deleted file mode 100644
index 1fa873c..0000000
--- a/base/cmsg.cpp
+++ /dev/null
@@ -1,173 +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 <android-base/cmsg.h>
-
-#include <errno.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <sys/socket.h>
-#include <sys/user.h>
-
-#include <memory>
-
-#include <android-base/logging.h>
-
-namespace android {
-namespace base {
-
-ssize_t SendFileDescriptorVector(borrowed_fd sockfd, const void* data, size_t len,
- const std::vector<int>& fds) {
- size_t cmsg_space = CMSG_SPACE(sizeof(int) * fds.size());
- size_t cmsg_len = CMSG_LEN(sizeof(int) * fds.size());
- if (cmsg_space >= PAGE_SIZE) {
- errno = ENOMEM;
- return -1;
- }
-
- alignas(struct cmsghdr) char cmsg_buf[cmsg_space];
- iovec iov = {.iov_base = const_cast<void*>(data), .iov_len = len};
- msghdr msg = {
- .msg_name = nullptr,
- .msg_namelen = 0,
- .msg_iov = &iov,
- .msg_iovlen = 1,
- .msg_control = cmsg_buf,
- // We can't cast to the actual type of the field, because it's different across platforms.
- .msg_controllen = static_cast<unsigned int>(cmsg_space),
- .msg_flags = 0,
- };
-
- struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- cmsg->cmsg_len = cmsg_len;
-
- int* cmsg_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
- for (size_t i = 0; i < fds.size(); ++i) {
- cmsg_fds[i] = fds[i];
- }
-
-#if defined(__linux__)
- int flags = MSG_NOSIGNAL;
-#else
- int flags = 0;
-#endif
-
- return TEMP_FAILURE_RETRY(sendmsg(sockfd.get(), &msg, flags));
-}
-
-ssize_t ReceiveFileDescriptorVector(borrowed_fd sockfd, void* data, size_t len, size_t max_fds,
- std::vector<unique_fd>* fds) {
- fds->clear();
-
- size_t cmsg_space = CMSG_SPACE(sizeof(int) * max_fds);
- if (cmsg_space >= PAGE_SIZE) {
- errno = ENOMEM;
- return -1;
- }
-
- alignas(struct cmsghdr) char cmsg_buf[cmsg_space];
- iovec iov = {.iov_base = const_cast<void*>(data), .iov_len = len};
- msghdr msg = {
- .msg_name = nullptr,
- .msg_namelen = 0,
- .msg_iov = &iov,
- .msg_iovlen = 1,
- .msg_control = cmsg_buf,
- // We can't cast to the actual type of the field, because it's different across platforms.
- .msg_controllen = static_cast<unsigned int>(cmsg_space),
- .msg_flags = 0,
- };
-
- int flags = MSG_TRUNC | MSG_CTRUNC;
-#if defined(__linux__)
- flags |= MSG_CMSG_CLOEXEC | MSG_NOSIGNAL;
-#endif
-
- ssize_t rc = TEMP_FAILURE_RETRY(recvmsg(sockfd.get(), &msg, flags));
-
- if (rc == -1) {
- return -1;
- }
-
- int error = 0;
- if ((msg.msg_flags & MSG_TRUNC)) {
- LOG(ERROR) << "message was truncated when receiving file descriptors";
- error = EMSGSIZE;
- } else if ((msg.msg_flags & MSG_CTRUNC)) {
- LOG(ERROR) << "control message was truncated when receiving file descriptors";
- error = EMSGSIZE;
- }
-
- std::vector<unique_fd> received_fds;
- struct cmsghdr* cmsg;
- for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != nullptr; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
- if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
- LOG(ERROR) << "received unexpected cmsg: [" << cmsg->cmsg_level << ", " << cmsg->cmsg_type
- << "]";
- error = EBADMSG;
- continue;
- }
-
- // There isn't a macro that does the inverse of CMSG_LEN, so hack around it ourselves, with
- // some asserts to ensure that CMSG_LEN behaves as we expect.
-#if defined(__linux__)
-#define CMSG_ASSERT static_assert
-#else
-// CMSG_LEN is somehow not constexpr on darwin.
-#define CMSG_ASSERT CHECK
-#endif
- CMSG_ASSERT(CMSG_LEN(0) + 1 * sizeof(int) == CMSG_LEN(1 * sizeof(int)));
- CMSG_ASSERT(CMSG_LEN(0) + 2 * sizeof(int) == CMSG_LEN(2 * sizeof(int)));
- CMSG_ASSERT(CMSG_LEN(0) + 3 * sizeof(int) == CMSG_LEN(3 * sizeof(int)));
- CMSG_ASSERT(CMSG_LEN(0) + 4 * sizeof(int) == CMSG_LEN(4 * sizeof(int)));
-
- if (cmsg->cmsg_len % sizeof(int) != 0) {
- LOG(FATAL) << "cmsg_len(" << cmsg->cmsg_len << ") not aligned to sizeof(int)";
- } else if (cmsg->cmsg_len <= CMSG_LEN(0)) {
- LOG(FATAL) << "cmsg_len(" << cmsg->cmsg_len << ") not long enough to hold any data";
- }
-
- int* cmsg_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
- size_t cmsg_fdcount = static_cast<size_t>(cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
- for (size_t i = 0; i < cmsg_fdcount; ++i) {
-#if !defined(__linux__)
- // Linux uses MSG_CMSG_CLOEXEC instead of doing this manually.
- fcntl(cmsg_fds[i], F_SETFD, FD_CLOEXEC);
-#endif
- received_fds.emplace_back(cmsg_fds[i]);
- }
- }
-
- if (error != 0) {
- errno = error;
- return -1;
- }
-
- if (received_fds.size() > max_fds) {
- LOG(ERROR) << "received too many file descriptors, expected " << fds->size() << ", received "
- << received_fds.size();
- errno = EMSGSIZE;
- return -1;
- }
-
- *fds = std::move(received_fds);
- return rc;
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/cmsg_test.cpp b/base/cmsg_test.cpp
deleted file mode 100644
index 9ee5c82..0000000
--- a/base/cmsg_test.cpp
+++ /dev/null
@@ -1,202 +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 <android-base/cmsg.h>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/unique_fd.h>
-#include <gtest/gtest.h>
-
-#if !defined(_WIN32)
-
-using android::base::ReceiveFileDescriptors;
-using android::base::SendFileDescriptors;
-using android::base::unique_fd;
-
-static ino_t GetInode(int fd) {
- struct stat st;
- if (fstat(fd, &st) != 0) {
- PLOG(FATAL) << "fstat failed";
- }
-
- return st.st_ino;
-}
-
-struct CmsgTest : ::testing::TestWithParam<bool> {
- bool Seqpacket() { return GetParam(); }
-
- void SetUp() override {
- ASSERT_TRUE(
- android::base::Socketpair(Seqpacket() ? SOCK_SEQPACKET : SOCK_STREAM, &send, &recv));
- int dup1 = dup(tmp1.fd);
- ASSERT_NE(-1, dup1);
- int dup2 = dup(tmp2.fd);
- ASSERT_NE(-1, dup2);
-
- fd1.reset(dup1);
- fd2.reset(dup2);
-
- ino1 = GetInode(dup1);
- ino2 = GetInode(dup2);
- }
-
- unique_fd send;
- unique_fd recv;
-
- TemporaryFile tmp1;
- TemporaryFile tmp2;
-
- unique_fd fd1;
- unique_fd fd2;
-
- ino_t ino1;
- ino_t ino2;
-};
-
-TEST_P(CmsgTest, smoke) {
- ASSERT_EQ(1, SendFileDescriptors(send.get(), "x", 1, fd1.get()));
-
- char buf[2];
- unique_fd received;
- ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 2, &received));
- ASSERT_EQ('x', buf[0]);
- ASSERT_NE(-1, received.get());
-
- ASSERT_EQ(ino1, GetInode(received.get()));
-}
-
-TEST_P(CmsgTest, msg_trunc) {
- ASSERT_EQ(2, SendFileDescriptors(send.get(), "ab", 2, fd1.get(), fd2.get()));
-
- char buf[2];
- unique_fd received1, received2;
-
- ssize_t rc = ReceiveFileDescriptors(recv.get(), buf, 1, &received1, &received2);
- if (Seqpacket()) {
- ASSERT_EQ(-1, rc);
- ASSERT_EQ(EMSGSIZE, errno);
- ASSERT_EQ(-1, received1.get());
- ASSERT_EQ(-1, received2.get());
- } else {
- ASSERT_EQ(1, rc);
- ASSERT_NE(-1, received1.get());
- ASSERT_NE(-1, received2.get());
- ASSERT_EQ(ino1, GetInode(received1.get()));
- ASSERT_EQ(ino2, GetInode(received2.get()));
- ASSERT_EQ(1, read(recv.get(), buf, 2));
- }
-}
-
-TEST_P(CmsgTest, msg_ctrunc) {
- ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get(), fd2.get()));
-
- char buf[2];
- unique_fd received;
- ASSERT_EQ(-1, ReceiveFileDescriptors(recv.get(), buf, 1, &received));
- ASSERT_EQ(EMSGSIZE, errno);
- ASSERT_EQ(-1, received.get());
-}
-
-TEST_P(CmsgTest, peek) {
- ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get()));
-
- char buf[2];
- ASSERT_EQ(1, ::recv(recv.get(), buf, sizeof(buf), MSG_PEEK));
- ASSERT_EQ('a', buf[0]);
-
- unique_fd received;
- ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received));
- ASSERT_EQ(ino1, GetInode(received.get()));
-}
-
-TEST_P(CmsgTest, stream_fd_association) {
- if (Seqpacket()) {
- return;
- }
-
- // fds are associated with the first byte of the write.
- ASSERT_EQ(1, TEMP_FAILURE_RETRY(write(send.get(), "a", 1)));
- ASSERT_EQ(2, SendFileDescriptors(send.get(), "bc", 2, fd1.get()));
- ASSERT_EQ(1, SendFileDescriptors(send.get(), "d", 1, fd2.get()));
- char buf[2];
- ASSERT_EQ(2, TEMP_FAILURE_RETRY(read(recv.get(), buf, 2)));
- ASSERT_EQ(0, memcmp(buf, "ab", 2));
-
- std::vector<unique_fd> received1;
- ssize_t rc = ReceiveFileDescriptorVector(recv.get(), buf, 1, 1, &received1);
- ASSERT_EQ(1, rc);
- ASSERT_EQ('c', buf[0]);
- ASSERT_TRUE(received1.empty());
-
- unique_fd received2;
- rc = ReceiveFileDescriptors(recv.get(), buf, 1, &received2);
- ASSERT_EQ(1, rc);
- ASSERT_EQ('d', buf[0]);
- ASSERT_EQ(ino2, GetInode(received2.get()));
-}
-
-TEST_P(CmsgTest, multiple_fd_ordering) {
- ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get(), fd2.get()));
-
- char buf[2];
- unique_fd received1, received2;
- ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received1, &received2));
-
- ASSERT_NE(-1, received1.get());
- ASSERT_NE(-1, received2.get());
-
- ASSERT_EQ(ino1, GetInode(received1.get()));
- ASSERT_EQ(ino2, GetInode(received2.get()));
-}
-
-TEST_P(CmsgTest, separate_fd_ordering) {
- ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get()));
- ASSERT_EQ(1, SendFileDescriptors(send.get(), "b", 1, fd2.get()));
-
- char buf[2];
- unique_fd received1, received2;
- ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received1));
- ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received2));
-
- ASSERT_NE(-1, received1.get());
- ASSERT_NE(-1, received2.get());
-
- ASSERT_EQ(ino1, GetInode(received1.get()));
- ASSERT_EQ(ino2, GetInode(received2.get()));
-}
-
-TEST_P(CmsgTest, separate_fds_no_coalescing) {
- unique_fd sent1(dup(tmp1.fd));
- unique_fd sent2(dup(tmp2.fd));
-
- ASSERT_EQ(1, SendFileDescriptors(send.get(), "", 1, fd1.get()));
- ASSERT_EQ(1, SendFileDescriptors(send.get(), "", 1, fd2.get()));
-
- char buf[2];
- std::vector<unique_fd> received;
- ASSERT_EQ(1, ReceiveFileDescriptorVector(recv.get(), buf, 2, 2, &received));
- ASSERT_EQ(1U, received.size());
- ASSERT_EQ(ino1, GetInode(received[0].get()));
-
- ASSERT_EQ(1, ReceiveFileDescriptorVector(recv.get(), buf, 2, 2, &received));
- ASSERT_EQ(1U, received.size());
- ASSERT_EQ(ino2, GetInode(received[0].get()));
-}
-
-INSTANTIATE_TEST_CASE_P(CmsgTest, CmsgTest, testing::Bool());
-
-#endif
diff --git a/base/endian_test.cpp b/base/endian_test.cpp
deleted file mode 100644
index 963ab13..0000000
--- a/base/endian_test.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * 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.
- */
-
-#include "android-base/endian.h"
-
-#include <gtest/gtest.h>
-
-TEST(endian, constants) {
- ASSERT_TRUE(__LITTLE_ENDIAN == LITTLE_ENDIAN);
- ASSERT_TRUE(__BIG_ENDIAN == BIG_ENDIAN);
- ASSERT_TRUE(__BYTE_ORDER == BYTE_ORDER);
-
- ASSERT_EQ(__LITTLE_ENDIAN, __BYTE_ORDER);
-}
-
-TEST(endian, smoke) {
- static constexpr uint16_t le16 = 0x1234;
- static constexpr uint32_t le32 = 0x12345678;
- static constexpr uint64_t le64 = 0x123456789abcdef0;
-
- static constexpr uint16_t be16 = 0x3412;
- static constexpr uint32_t be32 = 0x78563412;
- static constexpr uint64_t be64 = 0xf0debc9a78563412;
-
- ASSERT_EQ(be16, htons(le16));
- ASSERT_EQ(be32, htonl(le32));
- ASSERT_EQ(be64, htonq(le64));
-
- ASSERT_EQ(le16, ntohs(be16));
- ASSERT_EQ(le32, ntohl(be32));
- ASSERT_EQ(le64, ntohq(be64));
-
- ASSERT_EQ(be16, htobe16(le16));
- ASSERT_EQ(be32, htobe32(le32));
- ASSERT_EQ(be64, htobe64(le64));
-
- ASSERT_EQ(le16, betoh16(be16));
- ASSERT_EQ(le32, betoh32(be32));
- ASSERT_EQ(le64, betoh64(be64));
-
- ASSERT_EQ(le16, htole16(le16));
- ASSERT_EQ(le32, htole32(le32));
- ASSERT_EQ(le64, htole64(le64));
-
- ASSERT_EQ(le16, letoh16(le16));
- ASSERT_EQ(le32, letoh32(le32));
- ASSERT_EQ(le64, letoh64(le64));
-
- ASSERT_EQ(le16, be16toh(be16));
- ASSERT_EQ(le32, be32toh(be32));
- ASSERT_EQ(le64, be64toh(be64));
-
- ASSERT_EQ(le16, le16toh(le16));
- ASSERT_EQ(le32, le32toh(le32));
- ASSERT_EQ(le64, le64toh(le64));
-}
diff --git a/base/errors_test.cpp b/base/errors_test.cpp
deleted file mode 100644
index 8e7cdd1..0000000
--- a/base/errors_test.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/errors.h"
-
-#include <gtest/gtest.h>
-
-namespace android {
-namespace base {
-
-// Error strings aren't consistent enough across systems to test the output,
-// just make sure we can compile correctly and nothing crashes even if we send
-// it possibly bogus error codes.
-TEST(ErrorsTest, TestSystemErrorString) {
- SystemErrorCodeToString(-1);
- SystemErrorCodeToString(0);
- SystemErrorCodeToString(1);
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/errors_unix.cpp b/base/errors_unix.cpp
deleted file mode 100644
index 48269b6..0000000
--- a/base/errors_unix.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/errors.h"
-
-#include <errno.h>
-#include <string.h>
-
-namespace android {
-namespace base {
-
-std::string SystemErrorCodeToString(int error_code) {
- return strerror(error_code);
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/errors_windows.cpp b/base/errors_windows.cpp
deleted file mode 100644
index a5ff511..0000000
--- a/base/errors_windows.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/errors.h"
-
-#include <windows.h>
-
-#include "android-base/stringprintf.h"
-#include "android-base/strings.h"
-#include "android-base/utf8.h"
-
-// A Windows error code is a DWORD. It's simpler to use an int error code for
-// both Unix and Windows if possible, but if this fails we'll need a different
-// function signature for each.
-static_assert(sizeof(int) >= sizeof(DWORD),
- "Windows system error codes are too large to fit in an int.");
-
-namespace android {
-namespace base {
-
-static constexpr DWORD kErrorMessageBufferSize = 256;
-
-std::string SystemErrorCodeToString(int int_error_code) {
- WCHAR msgbuf[kErrorMessageBufferSize];
- DWORD error_code = int_error_code;
- DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
- DWORD len = FormatMessageW(flags, nullptr, error_code, 0, msgbuf,
- kErrorMessageBufferSize, nullptr);
- if (len == 0) {
- return android::base::StringPrintf(
- "Error %lu while retrieving message for error %lu", GetLastError(),
- error_code);
- }
-
- // Convert UTF-16 to UTF-8.
- std::string msg;
- if (!android::base::WideToUTF8(msgbuf, &msg)) {
- return android::base::StringPrintf(
- "Error %lu while converting message for error %lu from UTF-16 to UTF-8",
- GetLastError(), error_code);
- }
-
- // Messages returned by the system end with line breaks.
- msg = android::base::Trim(msg);
-
- // There are many Windows error messages compared to POSIX, so include the
- // numeric error code for easier, quicker, accurate identification. Use
- // decimal instead of hex because there are decimal ranges like 10000-11999
- // for Winsock.
- android::base::StringAppendF(&msg, " (%lu)", error_code);
- return msg;
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/expected_test.cpp b/base/expected_test.cpp
deleted file mode 100644
index 47e396a..0000000
--- a/base/expected_test.cpp
+++ /dev/null
@@ -1,876 +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 "android-base/expected.h"
-
-#include <cstdio>
-#include <memory>
-#include <string>
-
-#include <gtest/gtest.h>
-
-using android::base::expected;
-using android::base::unexpected;
-
-typedef expected<int, int> exp_int;
-typedef expected<double, double> exp_double;
-typedef expected<std::string, std::string> exp_string;
-typedef expected<std::pair<std::string, int>, int> exp_pair;
-typedef expected<void, int> exp_void;
-
-struct T {
- int a;
- int b;
- T() = default;
- T(int a, int b) noexcept : a(a), b(b) {}
-};
-bool operator==(const T& x, const T& y) {
- return x.a == y.a && x.b == y.b;
-}
-bool operator!=(const T& x, const T& y) {
- return x.a != y.a || x.b != y.b;
-}
-
-struct E {
- std::string message;
- int cause;
- E(const std::string& message, int cause) : message(message), cause(cause) {}
-};
-
-typedef expected<T,E> exp_complex;
-
-TEST(Expected, testDefaultConstructible) {
- exp_int e;
- EXPECT_TRUE(e.has_value());
- EXPECT_EQ(0, e.value());
-
- exp_complex e2;
- EXPECT_TRUE(e2.has_value());
- EXPECT_EQ(T(0,0), e2.value());
-
- exp_void e3;
- EXPECT_TRUE(e3.has_value());
-}
-
-TEST(Expected, testCopyConstructible) {
- exp_int e;
- exp_int e2 = e;
-
- EXPECT_TRUE(e.has_value());
- EXPECT_TRUE(e2.has_value());
- EXPECT_EQ(0, e.value());
- EXPECT_EQ(0, e2.value());
-
- exp_void e3;
- exp_void e4 = e3;
- EXPECT_TRUE(e3.has_value());
- EXPECT_TRUE(e4.has_value());
-}
-
-TEST(Expected, testMoveConstructible) {
- exp_int e;
- exp_int e2 = std::move(e);
-
- EXPECT_TRUE(e.has_value());
- EXPECT_TRUE(e2.has_value());
- EXPECT_EQ(0, e.value());
- EXPECT_EQ(0, e2.value());
-
- exp_string e3(std::string("hello"));
- exp_string e4 = std::move(e3);
-
- EXPECT_TRUE(e3.has_value());
- EXPECT_TRUE(e4.has_value());
- EXPECT_EQ("", e3.value()); // e3 is moved
- EXPECT_EQ("hello", e4.value());
-
- exp_void e5;
- exp_void e6 = std::move(e5);
- EXPECT_TRUE(e5.has_value());
- EXPECT_TRUE(e6.has_value());
-}
-
-TEST(Expected, testCopyConstructibleFromConvertibleType) {
- exp_double e = 3.3f;
- exp_int e2 = e;
-
- EXPECT_TRUE(e.has_value());
- EXPECT_TRUE(e2.has_value());
- EXPECT_EQ(3.3f, e.value());
- EXPECT_EQ(3, e2.value());
-}
-
-TEST(Expected, testMoveConstructibleFromConvertibleType) {
- exp_double e = 3.3f;
- exp_int e2 = std::move(e);
-
- EXPECT_TRUE(e.has_value());
- EXPECT_TRUE(e2.has_value());
- EXPECT_EQ(3.3f, e.value());
- EXPECT_EQ(3, e2.value());
-}
-
-TEST(Expected, testConstructibleFromValue) {
- exp_int e = 3;
- exp_double e2 = 5.5f;
- exp_string e3 = std::string("hello");
- exp_complex e4 = T(10, 20);
- exp_void e5 = {};
-
- EXPECT_TRUE(e.has_value());
- EXPECT_TRUE(e2.has_value());
- EXPECT_TRUE(e3.has_value());
- EXPECT_TRUE(e4.has_value());
- EXPECT_TRUE(e5.has_value());
- EXPECT_EQ(3, e.value());
- EXPECT_EQ(5.5f, e2.value());
- EXPECT_EQ("hello", e3.value());
- EXPECT_EQ(T(10,20), e4.value());
-}
-
-TEST(Expected, testConstructibleFromMovedValue) {
- std::string hello = "hello";
- exp_string e = std::move(hello);
-
- EXPECT_TRUE(e.has_value());
- EXPECT_EQ("hello", e.value());
- EXPECT_EQ("", hello);
-}
-
-TEST(Expected, testConstructibleFromConvertibleValue) {
- exp_int e = 3.3f; // double to int
- exp_string e2 = "hello"; // char* to std::string
- EXPECT_TRUE(e.has_value());
- EXPECT_EQ(3, e.value());
-
- EXPECT_TRUE(e2.has_value());
- EXPECT_EQ("hello", e2.value());
-}
-
-TEST(Expected, testConstructibleFromUnexpected) {
- exp_int::unexpected_type unexp = unexpected(10);
- exp_int e = unexp;
-
- exp_double::unexpected_type unexp2 = unexpected(10.5f);
- exp_double e2 = unexp2;
-
- exp_string::unexpected_type unexp3 = unexpected(std::string("error"));
- exp_string e3 = unexp3;
-
- exp_void::unexpected_type unexp4 = unexpected(10);
- exp_void e4 = unexp4;
-
- EXPECT_FALSE(e.has_value());
- EXPECT_FALSE(e2.has_value());
- EXPECT_FALSE(e3.has_value());
- EXPECT_FALSE(e4.has_value());
- EXPECT_EQ(10, e.error());
- EXPECT_EQ(10.5f, e2.error());
- EXPECT_EQ("error", e3.error());
- EXPECT_EQ(10, e4.error());
-}
-
-TEST(Expected, testMoveConstructibleFromUnexpected) {
- exp_int e = unexpected(10);
- exp_double e2 = unexpected(10.5f);
- exp_string e3 = unexpected(std::string("error"));
- exp_void e4 = unexpected(10);
-
- EXPECT_FALSE(e.has_value());
- EXPECT_FALSE(e2.has_value());
- EXPECT_FALSE(e3.has_value());
- EXPECT_FALSE(e4.has_value());
- EXPECT_EQ(10, e.error());
- EXPECT_EQ(10.5f, e2.error());
- EXPECT_EQ("error", e3.error());
- EXPECT_EQ(10, e4.error());
-}
-
-TEST(Expected, testConstructibleByForwarding) {
- exp_string e(std::in_place, 5, 'a');
- EXPECT_TRUE(e.has_value());
- EXPECT_EQ("aaaaa", e.value());
-
- exp_string e2({'a', 'b', 'c'});
- EXPECT_TRUE(e2.has_value());
- EXPECT_EQ("abc", e2.value());
-
- exp_pair e3({"hello", 30});
- EXPECT_TRUE(e3.has_value());
- EXPECT_EQ("hello",e3->first);
- EXPECT_EQ(30,e3->second);
-
- exp_void e4({});
- EXPECT_TRUE(e4.has_value());
-}
-
-TEST(Expected, testDestructible) {
- bool destroyed = false;
- struct T {
- bool* flag_;
- T(bool* flag) : flag_(flag) {}
- ~T() { *flag_ = true; }
- };
- {
- expected<T, int> exp = T(&destroyed);
- }
- EXPECT_TRUE(destroyed);
-}
-
-TEST(Expected, testAssignable) {
- exp_int e = 10;
- exp_int e2 = 20;
- e = e2;
-
- EXPECT_EQ(20, e.value());
- EXPECT_EQ(20, e2.value());
-
- exp_int e3 = 10;
- exp_int e4 = 20;
- e3 = std::move(e4);
-
- EXPECT_EQ(20, e3.value());
- EXPECT_EQ(20, e4.value());
-
- exp_void e5 = unexpected(10);
- ASSERT_FALSE(e5.has_value());
- exp_void e6;
- e5 = e6;
-
- EXPECT_TRUE(e5.has_value());
- EXPECT_TRUE(e6.has_value());
-}
-
-TEST(Expected, testAssignableFromValue) {
- exp_int e = 10;
- e = 20;
- EXPECT_EQ(20, e.value());
-
- exp_double e2 = 3.5f;
- e2 = 10.5f;
- EXPECT_EQ(10.5f, e2.value());
-
- exp_string e3 = "hello";
- e3 = "world";
- EXPECT_EQ("world", e3.value());
-
- exp_void e4 = unexpected(10);
- ASSERT_FALSE(e4.has_value());
- e4 = {};
- EXPECT_TRUE(e4.has_value());
-}
-
-TEST(Expected, testAssignableFromUnexpected) {
- exp_int e = 10;
- e = unexpected(30);
- EXPECT_FALSE(e.has_value());
- EXPECT_EQ(30, e.error());
-
- exp_double e2 = 3.5f;
- e2 = unexpected(10.5f);
- EXPECT_FALSE(e2.has_value());
- EXPECT_EQ(10.5f, e2.error());
-
- exp_string e3 = "hello";
- e3 = unexpected("world");
- EXPECT_FALSE(e3.has_value());
- EXPECT_EQ("world", e3.error());
-
- exp_void e4 = {};
- e4 = unexpected(10);
- EXPECT_FALSE(e4.has_value());
- EXPECT_EQ(10, e4.error());
-}
-
-TEST(Expected, testAssignableFromMovedValue) {
- std::string world = "world";
- exp_string e = "hello";
- e = std::move(world);
-
- EXPECT_TRUE(e.has_value());
- EXPECT_EQ("world", e.value());
- EXPECT_EQ("", world);
-}
-
-TEST(Expected, testAssignableFromMovedUnexpected) {
- std::string world = "world";
- exp_string e = "hello";
- e = unexpected(std::move(world));
-
- EXPECT_FALSE(e.has_value());
- EXPECT_EQ("world", e.error());
- EXPECT_EQ("", world);
-}
-
-TEST(Expected, testEmplace) {
- struct T {
- int a;
- double b;
- T() {}
- T(int a, double b) noexcept : a(a), b(b) {}
- };
- expected<T, int> exp;
- T& t = exp.emplace(3, 10.5f);
-
- EXPECT_TRUE(exp.has_value());
- EXPECT_EQ(3, t.a);
- EXPECT_EQ(10.5f, t.b);
- EXPECT_EQ(3, exp.value().a);
- EXPECT_EQ(10.5, exp.value().b);
-
- exp_void e = unexpected(10);
- ASSERT_FALSE(e.has_value());
- e.emplace();
- EXPECT_TRUE(e.has_value());
-}
-
-TEST(Expected, testSwapExpectedExpected) {
- exp_int e = 10;
- exp_int e2 = 20;
- e.swap(e2);
-
- EXPECT_TRUE(e.has_value());
- EXPECT_TRUE(e2.has_value());
- EXPECT_EQ(20, e.value());
- EXPECT_EQ(10, e2.value());
-
- exp_void e3;
- exp_void e4;
- e3.swap(e4);
-
- EXPECT_TRUE(e3.has_value());
- EXPECT_TRUE(e4.has_value());
-}
-
-TEST(Expected, testSwapUnexpectedUnexpected) {
- exp_int e = unexpected(10);
- exp_int e2 = unexpected(20);
- e.swap(e2);
- EXPECT_FALSE(e.has_value());
- EXPECT_FALSE(e2.has_value());
- EXPECT_EQ(20, e.error());
- EXPECT_EQ(10, e2.error());
-
- exp_void e3 = unexpected(10);
- exp_void e4 = unexpected(20);
- e3.swap(e4);
- EXPECT_FALSE(e3.has_value());
- EXPECT_FALSE(e4.has_value());
- EXPECT_EQ(20, e3.error());
- EXPECT_EQ(10, e4.error());
-}
-
-TEST(Expected, testSwapExpectedUnepected) {
- exp_int e = 10;
- exp_int e2 = unexpected(30);
- e.swap(e2);
- EXPECT_FALSE(e.has_value());
- EXPECT_TRUE(e2.has_value());
- EXPECT_EQ(30, e.error());
- EXPECT_EQ(10, e2.value());
-
- exp_void e3;
- exp_void e4 = unexpected(10);
- e3.swap(e4);
- EXPECT_FALSE(e3.has_value());
- EXPECT_TRUE(e4.has_value());
- EXPECT_EQ(10, e3.error());
-}
-
-TEST(Expected, testDereference) {
- struct T {
- int a;
- double b;
- T() {}
- T(int a, double b) : a(a), b(b) {}
- };
- expected<T, int> exp = T(3, 10.5f);
-
- EXPECT_EQ(3, exp->a);
- EXPECT_EQ(10.5f, exp->b);
-
- EXPECT_EQ(3, (*exp).a);
- EXPECT_EQ(10.5f, (*exp).b);
-}
-
-TEST(Expected, testTest) {
- exp_int e = 10;
- EXPECT_TRUE(e.ok());
- EXPECT_TRUE(e.has_value());
-
- exp_int e2 = unexpected(10);
- EXPECT_FALSE(e2.ok());
- EXPECT_FALSE(e2.has_value());
-}
-
-TEST(Expected, testGetValue) {
- exp_int e = 10;
- EXPECT_EQ(10, e.value());
- EXPECT_EQ(10, e.value_or(20));
-
- exp_int e2 = unexpected(10);
- EXPECT_EQ(10, e2.error());
- EXPECT_EQ(20, e2.value_or(20));
-}
-
-TEST(Expected, testSameValues) {
- exp_int e = 10;
- exp_int e2 = 10;
- EXPECT_TRUE(e == e2);
- EXPECT_TRUE(e2 == e);
- EXPECT_FALSE(e != e2);
- EXPECT_FALSE(e2 != e);
-
- exp_void e3;
- exp_void e4;
- EXPECT_TRUE(e3 == e4);
- EXPECT_TRUE(e4 == e3);
- EXPECT_FALSE(e3 != e4);
- EXPECT_FALSE(e4 != e3);
-}
-
-TEST(Expected, testDifferentValues) {
- exp_int e = 10;
- exp_int e2 = 20;
- EXPECT_FALSE(e == e2);
- EXPECT_FALSE(e2 == e);
- EXPECT_TRUE(e != e2);
- EXPECT_TRUE(e2 != e);
-}
-
-TEST(Expected, testValueWithError) {
- exp_int e = 10;
- exp_int e2 = unexpected(10);
- EXPECT_FALSE(e == e2);
- EXPECT_FALSE(e2 == e);
- EXPECT_TRUE(e != e2);
- EXPECT_TRUE(e2 != e);
-
- exp_void e3;
- exp_void e4 = unexpected(10);
- EXPECT_FALSE(e3 == e4);
- EXPECT_FALSE(e4 == e3);
- EXPECT_TRUE(e3 != e4);
- EXPECT_TRUE(e4 != e3);
-}
-
-TEST(Expected, testSameErrors) {
- exp_int e = unexpected(10);
- exp_int e2 = unexpected(10);
- EXPECT_TRUE(e == e2);
- EXPECT_TRUE(e2 == e);
- EXPECT_FALSE(e != e2);
- EXPECT_FALSE(e2 != e);
-
- exp_void e3 = unexpected(10);
- exp_void e4 = unexpected(10);
- EXPECT_TRUE(e3 == e4);
- EXPECT_TRUE(e4 == e3);
- EXPECT_FALSE(e3 != e4);
- EXPECT_FALSE(e4 != e3);
-}
-
-TEST(Expected, testDifferentErrors) {
- exp_int e = unexpected(10);
- exp_int e2 = unexpected(20);
- EXPECT_FALSE(e == e2);
- EXPECT_FALSE(e2 == e);
- EXPECT_TRUE(e != e2);
- EXPECT_TRUE(e2 != e);
-
- exp_void e3 = unexpected(10);
- exp_void e4 = unexpected(20);
- EXPECT_FALSE(e3 == e4);
- EXPECT_FALSE(e4 == e3);
- EXPECT_TRUE(e3 != e4);
- EXPECT_TRUE(e4 != e3);
-}
-
-TEST(Expected, testCompareWithSameError) {
- exp_int e = unexpected(10);
- exp_int::unexpected_type error = 10;
- EXPECT_TRUE(e == error);
- EXPECT_TRUE(error == e);
- EXPECT_FALSE(e != error);
- EXPECT_FALSE(error != e);
-
- exp_void e2 = unexpected(10);
- exp_void::unexpected_type error2 = 10;
- EXPECT_TRUE(e2 == error2);
- EXPECT_TRUE(error2 == e2);
- EXPECT_FALSE(e2 != error2);
- EXPECT_FALSE(error2 != e2);
-}
-
-TEST(Expected, testCompareWithDifferentError) {
- exp_int e = unexpected(10);
- exp_int::unexpected_type error = 20;
- EXPECT_FALSE(e == error);
- EXPECT_FALSE(error == e);
- EXPECT_TRUE(e != error);
- EXPECT_TRUE(error != e);
-
- exp_void e2 = unexpected(10);
- exp_void::unexpected_type error2 = 20;
- EXPECT_FALSE(e2 == error2);
- EXPECT_FALSE(error2 == e2);
- EXPECT_TRUE(e2 != error2);
- EXPECT_TRUE(error2 != e2);
-}
-
-TEST(Expected, testCompareDifferentType) {
- expected<int,int> e = 10;
- expected<int32_t, int> e2 = 10;
- EXPECT_TRUE(e == e2);
- e2 = 20;
- EXPECT_FALSE(e == e2);
-
- expected<std::string_view,int> e3 = "hello";
- expected<std::string,int> e4 = "hello";
- EXPECT_TRUE(e3 == e4);
- e4 = "world";
- EXPECT_FALSE(e3 == e4);
-
- expected<void,int> e5;
- expected<int,int> e6 = 10;
- EXPECT_FALSE(e5 == e6);
- EXPECT_FALSE(e6 == e5);
-}
-
-TEST(Expected, testDivideExample) {
- struct QR {
- int quotient;
- int remainder;
- QR(int q, int r) noexcept : quotient(q), remainder(r) {}
- bool operator==(const QR& rhs) const {
- return quotient == rhs.quotient && remainder == rhs.remainder;
- }
- bool operator!=(const QR& rhs) const {
- return quotient != rhs.quotient || remainder == rhs.remainder;
- }
- };
-
- auto divide = [](int x, int y) -> expected<QR,E> {
- if (y == 0) {
- return unexpected(E("divide by zero", -1));
- } else {
- return QR(x / y, x % y);
- }
- };
-
- EXPECT_FALSE(divide(10, 0).ok());
- EXPECT_EQ("divide by zero", divide(10, 0).error().message);
- EXPECT_EQ(-1, divide(10, 0).error().cause);
-
- EXPECT_TRUE(divide(10, 3).ok());
- EXPECT_EQ(QR(3, 1), *divide(10, 3));
-}
-
-TEST(Expected, testPair) {
- auto test = [](bool yes) -> exp_pair {
- if (yes) {
- return exp_pair({"yes", 42});
- } else {
- return unexpected(42);
- }
- };
-
- auto r = test(true);
- EXPECT_TRUE(r.ok());
- EXPECT_EQ("yes", r->first);
-}
-
-TEST(Expected, testVoid) {
- auto test = [](bool ok) -> exp_void {
- if (ok) {
- return {};
- } else {
- return unexpected(10);
- }
- };
-
- auto r = test(true);
- EXPECT_TRUE(r.ok());
- r = test(false);
- EXPECT_FALSE(r.ok());
- EXPECT_EQ(10, r.error());
-}
-
-// copied from result_test.cpp
-struct ConstructorTracker {
- static size_t constructor_called;
- static size_t copy_constructor_called;
- static size_t move_constructor_called;
- static size_t copy_assignment_called;
- static size_t move_assignment_called;
-
- template <typename T,
- typename std::enable_if_t<std::is_convertible_v<T, std::string>>* = nullptr>
- ConstructorTracker(T&& string) : string(string) {
- ++constructor_called;
- }
- ConstructorTracker(const ConstructorTracker& ct) {
- ++copy_constructor_called;
- string = ct.string;
- }
- ConstructorTracker(ConstructorTracker&& ct) noexcept {
- ++move_constructor_called;
- string = std::move(ct.string);
- }
- ConstructorTracker& operator=(const ConstructorTracker& ct) {
- ++copy_assignment_called;
- string = ct.string;
- return *this;
- }
- ConstructorTracker& operator=(ConstructorTracker&& ct) noexcept {
- ++move_assignment_called;
- string = std::move(ct.string);
- return *this;
- }
- static void Reset() {
- constructor_called = 0;
- copy_constructor_called = 0;
- move_constructor_called = 0;
- copy_assignment_called = 0;
- move_assignment_called = 0;
- }
- std::string string;
-};
-
-size_t ConstructorTracker::constructor_called = 0;
-size_t ConstructorTracker::copy_constructor_called = 0;
-size_t ConstructorTracker::move_constructor_called = 0;
-size_t ConstructorTracker::copy_assignment_called = 0;
-size_t ConstructorTracker::move_assignment_called = 0;
-
-typedef expected<ConstructorTracker, int> exp_track;
-
-TEST(Expected, testNumberOfCopies) {
- // default constructor
- ConstructorTracker::Reset();
- exp_track e("hello");
- EXPECT_EQ(1U, ConstructorTracker::constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
- EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
-
- // copy constructor
- ConstructorTracker::Reset();
- exp_track e2 = e;
- EXPECT_EQ(0U, ConstructorTracker::constructor_called);
- EXPECT_EQ(1U, ConstructorTracker::copy_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
- EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
-
- // move constructor
- ConstructorTracker::Reset();
- exp_track e3 = std::move(e);
- EXPECT_EQ(0U, ConstructorTracker::constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
- EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
- EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
-
- // construct from lvalue
- ConstructorTracker::Reset();
- ConstructorTracker ct = "hello";
- exp_track e4(ct);
- EXPECT_EQ(1U, ConstructorTracker::constructor_called);
- EXPECT_EQ(1U, ConstructorTracker::copy_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
- EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
-
- // construct from rvalue
- ConstructorTracker::Reset();
- ConstructorTracker ct2 = "hello";
- exp_track e5(std::move(ct2));
- EXPECT_EQ(1U, ConstructorTracker::constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
- EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
- EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
-
- // copy assignment
- ConstructorTracker::Reset();
- exp_track e6 = "hello";
- exp_track e7 = "world";
- e7 = e6;
- EXPECT_EQ(2U, ConstructorTracker::constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
- EXPECT_EQ(1U, ConstructorTracker::copy_assignment_called);
- EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
-
- // move assignment
- ConstructorTracker::Reset();
- exp_track e8 = "hello";
- exp_track e9 = "world";
- e9 = std::move(e8);
- EXPECT_EQ(2U, ConstructorTracker::constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
- EXPECT_EQ(1U, ConstructorTracker::move_assignment_called);
-
- // swap
- ConstructorTracker::Reset();
- exp_track e10 = "hello";
- exp_track e11 = "world";
- std::swap(e10, e11);
- EXPECT_EQ(2U, ConstructorTracker::constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
- EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
- EXPECT_EQ(2U, ConstructorTracker::move_assignment_called);
-}
-
-TEST(Expected, testNoCopyOnReturn) {
- auto test = [](const std::string& in) -> exp_track {
- if (in.empty()) {
- return "literal string";
- }
- if (in == "test2") {
- return ConstructorTracker(in + in + "2");
- }
- ConstructorTracker result(in + " " + in);
- return result;
- };
-
- ConstructorTracker::Reset();
- auto result1 = test("");
- ASSERT_TRUE(result1.ok());
- EXPECT_EQ("literal string", result1->string);
- EXPECT_EQ(1U, ConstructorTracker::constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
- EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
-
- ConstructorTracker::Reset();
- auto result2 = test("test2");
- ASSERT_TRUE(result2.ok());
- EXPECT_EQ("test2test22", result2->string);
- EXPECT_EQ(1U, ConstructorTracker::constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
- EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
- EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
-
- ConstructorTracker::Reset();
- auto result3 = test("test3");
- ASSERT_TRUE(result3.ok());
- EXPECT_EQ("test3 test3", result3->string);
- EXPECT_EQ(1U, ConstructorTracker::constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
- EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
- EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
-}
-
-TEST(Expected, testNested) {
- expected<exp_string, std::string> e = "hello";
-
- EXPECT_TRUE(e.ok());
- EXPECT_TRUE(e.has_value());
- EXPECT_TRUE(e.value().has_value());
- EXPECT_TRUE(e->ok());
- EXPECT_EQ("hello", e.value().value());
-
- expected<exp_string, std::string> e2 = unexpected("world");
- EXPECT_FALSE(e2.has_value());
- EXPECT_FALSE(e2.ok());
- EXPECT_EQ("world", e2.error());
-
- expected<exp_string, std::string> e3 = exp_string(unexpected("world"));
- EXPECT_TRUE(e3.has_value());
- EXPECT_FALSE(e3.value().has_value());
- EXPECT_TRUE(e3.ok());
- EXPECT_FALSE(e3->ok());
- EXPECT_EQ("world", e3.value().error());
-}
-
-constexpr bool equals(const char* a, const char* b) {
- return (a == nullptr && b == nullptr) ||
- (a != nullptr && b != nullptr && *a == *b &&
- (*a == '\0' || equals(a + 1, b + 1)));
-}
-
-TEST(Expected, testConstexpr) {
- // Compliation error will occur if these expressions can't be
- // evaluated at compile time
- constexpr exp_int e(3);
- constexpr exp_int::unexpected_type err(3);
- constexpr int i = 4;
-
- // default constructor
- static_assert(exp_int().value() == 0);
- // copy constructor
- static_assert(exp_int(e).value() == 3);
- // move constructor
- static_assert(exp_int(exp_int(4)).value() == 4);
- // copy construct from value
- static_assert(exp_int(i).value() == 4);
- // copy construct from unexpected
- static_assert(exp_int(err).error() == 3);
- // move costruct from unexpected
- static_assert(exp_int(unexpected(3)).error() == 3);
- // observers
- static_assert(*exp_int(3) == 3);
- static_assert(exp_int(3).has_value() == true);
- static_assert(exp_int(3).value_or(4) == 3);
-
- typedef expected<const char*, int> exp_s;
- constexpr exp_s s("hello");
- constexpr const char* c = "hello";
- static_assert(equals(exp_s().value(), nullptr));
- static_assert(equals(exp_s(s).value(), "hello"));
- static_assert(equals(exp_s(exp_s("hello")).value(), "hello"));
- static_assert(equals(exp_s("hello").value(), "hello"));
- static_assert(equals(exp_s(c).value(), "hello"));
-}
-
-TEST(Expected, testWithNonConstructible) {
- struct AssertNotConstructed {
- AssertNotConstructed() = delete;
- };
-
- expected<int, AssertNotConstructed> v(42);
- EXPECT_TRUE(v.has_value());
- EXPECT_EQ(42, v.value());
-
- expected<AssertNotConstructed, int> e(unexpected(42));
- EXPECT_FALSE(e.has_value());
- EXPECT_EQ(42, e.error());
-}
-
-TEST(Expected, testWithMoveOnlyType) {
- typedef expected<std::unique_ptr<int>,std::unique_ptr<int>> exp_ptr;
- exp_ptr e(std::make_unique<int>(3));
- exp_ptr e2(unexpected(std::make_unique<int>(4)));
-
- EXPECT_TRUE(e.has_value());
- EXPECT_FALSE(e2.has_value());
- EXPECT_EQ(3, *(e.value()));
- EXPECT_EQ(4, *(e2.error()));
-
- e2 = std::move(e);
- EXPECT_TRUE(e.has_value());
- EXPECT_TRUE(e2.has_value());
- EXPECT_EQ(3, *(e2.value()));
-}
diff --git a/base/file.cpp b/base/file.cpp
deleted file mode 100644
index 6321fc6..0000000
--- a/base/file.cpp
+++ /dev/null
@@ -1,522 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/file.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <ftw.h>
-#include <libgen.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <memory>
-#include <mutex>
-#include <string>
-#include <vector>
-
-#if defined(__APPLE__)
-#include <mach-o/dyld.h>
-#endif
-#if defined(_WIN32)
-#include <direct.h>
-#include <windows.h>
-#define O_NOFOLLOW 0
-#define OS_PATH_SEPARATOR '\\'
-#else
-#define OS_PATH_SEPARATOR '/'
-#endif
-
-#include "android-base/logging.h" // and must be after windows.h for ERROR
-#include "android-base/macros.h" // For TEMP_FAILURE_RETRY on Darwin.
-#include "android-base/unique_fd.h"
-#include "android-base/utf8.h"
-
-namespace {
-
-#ifdef _WIN32
-static int mkstemp(char* name_template, size_t size_in_chars) {
- std::wstring path;
- CHECK(android::base::UTF8ToWide(name_template, &path))
- << "path can't be converted to wchar: " << name_template;
- if (_wmktemp_s(path.data(), path.size() + 1) != 0) {
- return -1;
- }
-
- // Use open() to match the close() that TemporaryFile's destructor does.
- // Use O_BINARY to match base file APIs.
- int fd = _wopen(path.c_str(), O_CREAT | O_EXCL | O_RDWR | O_BINARY, S_IRUSR | S_IWUSR);
- if (fd < 0) {
- return -1;
- }
-
- std::string path_utf8;
- CHECK(android::base::WideToUTF8(path, &path_utf8)) << "path can't be converted to utf8";
- CHECK(strcpy_s(name_template, size_in_chars, path_utf8.c_str()) == 0)
- << "utf8 path can't be assigned back to name_template";
-
- return fd;
-}
-
-static char* mkdtemp(char* name_template, size_t size_in_chars) {
- std::wstring path;
- CHECK(android::base::UTF8ToWide(name_template, &path))
- << "path can't be converted to wchar: " << name_template;
-
- if (_wmktemp_s(path.data(), path.size() + 1) != 0) {
- return nullptr;
- }
-
- if (_wmkdir(path.c_str()) != 0) {
- return nullptr;
- }
-
- std::string path_utf8;
- CHECK(android::base::WideToUTF8(path, &path_utf8)) << "path can't be converted to utf8";
- CHECK(strcpy_s(name_template, size_in_chars, path_utf8.c_str()) == 0)
- << "utf8 path can't be assigned back to name_template";
-
- return name_template;
-}
-#endif
-
-std::string GetSystemTempDir() {
-#if defined(__ANDROID__)
- const auto* tmpdir = getenv("TMPDIR");
- if (tmpdir == nullptr) tmpdir = "/data/local/tmp";
- if (access(tmpdir, R_OK | W_OK | X_OK) == 0) {
- return tmpdir;
- }
- // Tests running in app context can't access /data/local/tmp,
- // so try current directory if /data/local/tmp is not accessible.
- return ".";
-#elif defined(_WIN32)
- wchar_t tmp_dir_w[MAX_PATH];
- DWORD result = GetTempPathW(std::size(tmp_dir_w), tmp_dir_w); // checks TMP env
- CHECK_NE(result, 0ul) << "GetTempPathW failed, error: " << GetLastError();
- CHECK_LT(result, std::size(tmp_dir_w)) << "path truncated to: " << result;
-
- // GetTempPath() returns a path with a trailing slash, but init()
- // does not expect that, so remove it.
- if (tmp_dir_w[result - 1] == L'\\') {
- tmp_dir_w[result - 1] = L'\0';
- }
-
- std::string tmp_dir;
- CHECK(android::base::WideToUTF8(tmp_dir_w, &tmp_dir)) << "path can't be converted to utf8";
-
- return tmp_dir;
-#else
- const auto* tmpdir = getenv("TMPDIR");
- if (tmpdir == nullptr) tmpdir = "/tmp";
- return tmpdir;
-#endif
-}
-
-} // namespace
-
-TemporaryFile::TemporaryFile() {
- init(GetSystemTempDir());
-}
-
-TemporaryFile::TemporaryFile(const std::string& tmp_dir) {
- init(tmp_dir);
-}
-
-TemporaryFile::~TemporaryFile() {
- if (fd != -1) {
- close(fd);
- }
- if (remove_file_) {
- unlink(path);
- }
-}
-
-int TemporaryFile::release() {
- int result = fd;
- fd = -1;
- return result;
-}
-
-void TemporaryFile::init(const std::string& tmp_dir) {
- snprintf(path, sizeof(path), "%s%cTemporaryFile-XXXXXX", tmp_dir.c_str(), OS_PATH_SEPARATOR);
-#if defined(_WIN32)
- fd = mkstemp(path, sizeof(path));
-#else
- fd = mkstemp(path);
-#endif
-}
-
-TemporaryDir::TemporaryDir() {
- init(GetSystemTempDir());
-}
-
-TemporaryDir::~TemporaryDir() {
- if (!remove_dir_and_contents_) return;
-
- auto callback = [](const char* child, const struct stat*, int file_type, struct FTW*) -> int {
- switch (file_type) {
- case FTW_D:
- case FTW_DP:
- case FTW_DNR:
- if (rmdir(child) == -1) {
- PLOG(ERROR) << "rmdir " << child;
- }
- break;
- case FTW_NS:
- default:
- if (rmdir(child) != -1) break;
- // FALLTHRU (for gcc, lint, pcc, etc; and following for clang)
- FALLTHROUGH_INTENDED;
- case FTW_F:
- case FTW_SL:
- case FTW_SLN:
- if (unlink(child) == -1) {
- PLOG(ERROR) << "unlink " << child;
- }
- break;
- }
- return 0;
- };
-
- nftw(path, callback, 128, FTW_DEPTH | FTW_MOUNT | FTW_PHYS);
-}
-
-bool TemporaryDir::init(const std::string& tmp_dir) {
- snprintf(path, sizeof(path), "%s%cTemporaryDir-XXXXXX", tmp_dir.c_str(), OS_PATH_SEPARATOR);
-#if defined(_WIN32)
- return (mkdtemp(path, sizeof(path)) != nullptr);
-#else
- return (mkdtemp(path) != nullptr);
-#endif
-}
-
-namespace android {
-namespace base {
-
-// Versions of standard library APIs that support UTF-8 strings.
-using namespace android::base::utf8;
-
-bool ReadFdToString(borrowed_fd fd, std::string* content) {
- content->clear();
-
- // Although original we had small files in mind, this code gets used for
- // very large files too, where the std::string growth heuristics might not
- // be suitable. https://code.google.com/p/android/issues/detail?id=258500.
- struct stat sb;
- if (fstat(fd.get(), &sb) != -1 && sb.st_size > 0) {
- content->reserve(sb.st_size);
- }
-
- char buf[BUFSIZ];
- ssize_t n;
- while ((n = TEMP_FAILURE_RETRY(read(fd.get(), &buf[0], sizeof(buf)))) > 0) {
- content->append(buf, n);
- }
- return (n == 0) ? true : false;
-}
-
-bool ReadFileToString(const std::string& path, std::string* content, bool follow_symlinks) {
- content->clear();
-
- int flags = O_RDONLY | O_CLOEXEC | O_BINARY | (follow_symlinks ? 0 : O_NOFOLLOW);
- android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags)));
- if (fd == -1) {
- return false;
- }
- return ReadFdToString(fd, content);
-}
-
-bool WriteStringToFd(const std::string& content, borrowed_fd fd) {
- const char* p = content.data();
- size_t left = content.size();
- while (left > 0) {
- ssize_t n = TEMP_FAILURE_RETRY(write(fd.get(), p, left));
- if (n == -1) {
- return false;
- }
- p += n;
- left -= n;
- }
- return true;
-}
-
-static bool CleanUpAfterFailedWrite(const std::string& path) {
- // Something went wrong. Let's not leave a corrupt file lying around.
- int saved_errno = errno;
- unlink(path.c_str());
- errno = saved_errno;
- return false;
-}
-
-#if !defined(_WIN32)
-bool WriteStringToFile(const std::string& content, const std::string& path,
- mode_t mode, uid_t owner, gid_t group,
- bool follow_symlinks) {
- int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY |
- (follow_symlinks ? 0 : O_NOFOLLOW);
- android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode)));
- if (fd == -1) {
- PLOG(ERROR) << "android::WriteStringToFile open failed";
- return false;
- }
-
- // We do an explicit fchmod here because we assume that the caller really
- // meant what they said and doesn't want the umask-influenced mode.
- if (fchmod(fd, mode) == -1) {
- PLOG(ERROR) << "android::WriteStringToFile fchmod failed";
- return CleanUpAfterFailedWrite(path);
- }
- if (fchown(fd, owner, group) == -1) {
- PLOG(ERROR) << "android::WriteStringToFile fchown failed";
- return CleanUpAfterFailedWrite(path);
- }
- if (!WriteStringToFd(content, fd)) {
- PLOG(ERROR) << "android::WriteStringToFile write failed";
- return CleanUpAfterFailedWrite(path);
- }
- return true;
-}
-#endif
-
-bool WriteStringToFile(const std::string& content, const std::string& path,
- bool follow_symlinks) {
- int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY |
- (follow_symlinks ? 0 : O_NOFOLLOW);
- android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags, 0666)));
- if (fd == -1) {
- return false;
- }
- return WriteStringToFd(content, fd) || CleanUpAfterFailedWrite(path);
-}
-
-bool ReadFully(borrowed_fd fd, void* data, size_t byte_count) {
- uint8_t* p = reinterpret_cast<uint8_t*>(data);
- size_t remaining = byte_count;
- while (remaining > 0) {
- ssize_t n = TEMP_FAILURE_RETRY(read(fd.get(), p, remaining));
- if (n <= 0) return false;
- p += n;
- remaining -= n;
- }
- return true;
-}
-
-#if defined(_WIN32)
-// Windows implementation of pread. Note that this DOES move the file descriptors read position,
-// but it does so atomically.
-static ssize_t pread(borrowed_fd fd, void* data, size_t byte_count, off64_t offset) {
- DWORD bytes_read;
- OVERLAPPED overlapped;
- memset(&overlapped, 0, sizeof(OVERLAPPED));
- overlapped.Offset = static_cast<DWORD>(offset);
- overlapped.OffsetHigh = static_cast<DWORD>(offset >> 32);
- if (!ReadFile(reinterpret_cast<HANDLE>(_get_osfhandle(fd.get())), data,
- static_cast<DWORD>(byte_count), &bytes_read, &overlapped)) {
- // In case someone tries to read errno (since this is masquerading as a POSIX call)
- errno = EIO;
- return -1;
- }
- return static_cast<ssize_t>(bytes_read);
-}
-#endif
-
-bool ReadFullyAtOffset(borrowed_fd fd, void* data, size_t byte_count, off64_t offset) {
- uint8_t* p = reinterpret_cast<uint8_t*>(data);
- while (byte_count > 0) {
- ssize_t n = TEMP_FAILURE_RETRY(pread(fd.get(), p, byte_count, offset));
- if (n <= 0) return false;
- p += n;
- byte_count -= n;
- offset += n;
- }
- return true;
-}
-
-bool WriteFully(borrowed_fd fd, const void* data, size_t byte_count) {
- const uint8_t* p = reinterpret_cast<const uint8_t*>(data);
- size_t remaining = byte_count;
- while (remaining > 0) {
- ssize_t n = TEMP_FAILURE_RETRY(write(fd.get(), p, remaining));
- if (n == -1) return false;
- p += n;
- remaining -= n;
- }
- return true;
-}
-
-bool RemoveFileIfExists(const std::string& path, std::string* err) {
- struct stat st;
-#if defined(_WIN32)
- // TODO: Windows version can't handle symbolic links correctly.
- int result = stat(path.c_str(), &st);
- bool file_type_removable = (result == 0 && S_ISREG(st.st_mode));
-#else
- int result = lstat(path.c_str(), &st);
- bool file_type_removable = (result == 0 && (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)));
-#endif
- if (result == -1) {
- if (errno == ENOENT || errno == ENOTDIR) return true;
- if (err != nullptr) *err = strerror(errno);
- return false;
- }
-
- if (result == 0) {
- if (!file_type_removable) {
- if (err != nullptr) {
- *err = "is not a regular file or symbolic link";
- }
- return false;
- }
- if (unlink(path.c_str()) == -1) {
- if (err != nullptr) {
- *err = strerror(errno);
- }
- return false;
- }
- }
- return true;
-}
-
-#if !defined(_WIN32)
-bool Readlink(const std::string& path, std::string* result) {
- result->clear();
-
- // Most Linux file systems (ext2 and ext4, say) limit symbolic links to
- // 4095 bytes. Since we'll copy out into the string anyway, it doesn't
- // waste memory to just start there. We add 1 so that we can recognize
- // whether it actually fit (rather than being truncated to 4095).
- std::vector<char> buf(4095 + 1);
- while (true) {
- ssize_t size = readlink(path.c_str(), &buf[0], buf.size());
- // Unrecoverable error?
- if (size == -1) return false;
- // It fit! (If size == buf.size(), it may have been truncated.)
- if (static_cast<size_t>(size) < buf.size()) {
- result->assign(&buf[0], size);
- return true;
- }
- // Double our buffer and try again.
- buf.resize(buf.size() * 2);
- }
-}
-#endif
-
-#if !defined(_WIN32)
-bool Realpath(const std::string& path, std::string* result) {
- result->clear();
-
- // realpath may exit with EINTR. Retry if so.
- char* realpath_buf = nullptr;
- do {
- realpath_buf = realpath(path.c_str(), nullptr);
- } while (realpath_buf == nullptr && errno == EINTR);
-
- if (realpath_buf == nullptr) {
- return false;
- }
- result->assign(realpath_buf);
- free(realpath_buf);
- return true;
-}
-#endif
-
-std::string GetExecutablePath() {
-#if defined(__linux__)
- std::string path;
- android::base::Readlink("/proc/self/exe", &path);
- return path;
-#elif defined(__APPLE__)
- char path[PATH_MAX + 1];
- uint32_t path_len = sizeof(path);
- int rc = _NSGetExecutablePath(path, &path_len);
- if (rc < 0) {
- std::unique_ptr<char> path_buf(new char[path_len]);
- _NSGetExecutablePath(path_buf.get(), &path_len);
- return path_buf.get();
- }
- return path;
-#elif defined(_WIN32)
- char path[PATH_MAX + 1];
- DWORD result = GetModuleFileName(NULL, path, sizeof(path) - 1);
- if (result == 0 || result == sizeof(path) - 1) return "";
- path[PATH_MAX - 1] = 0;
- return path;
-#else
-#error unknown OS
-#endif
-}
-
-std::string GetExecutableDirectory() {
- return Dirname(GetExecutablePath());
-}
-
-std::string Basename(const std::string& path) {
- // Copy path because basename may modify the string passed in.
- std::string result(path);
-
-#if !defined(__BIONIC__)
- // Use lock because basename() may write to a process global and return a
- // pointer to that. Note that this locking strategy only works if all other
- // callers to basename in the process also grab this same lock, but its
- // better than nothing. Bionic's basename returns a thread-local buffer.
- static std::mutex& basename_lock = *new std::mutex();
- std::lock_guard<std::mutex> lock(basename_lock);
-#endif
-
- // Note that if std::string uses copy-on-write strings, &str[0] will cause
- // the copy to be made, so there is no chance of us accidentally writing to
- // the storage for 'path'.
- char* name = basename(&result[0]);
-
- // In case basename returned a pointer to a process global, copy that string
- // before leaving the lock.
- result.assign(name);
-
- return result;
-}
-
-std::string Dirname(const std::string& path) {
- // Copy path because dirname may modify the string passed in.
- std::string result(path);
-
-#if !defined(__BIONIC__)
- // Use lock because dirname() may write to a process global and return a
- // pointer to that. Note that this locking strategy only works if all other
- // callers to dirname in the process also grab this same lock, but its
- // better than nothing. Bionic's dirname returns a thread-local buffer.
- static std::mutex& dirname_lock = *new std::mutex();
- std::lock_guard<std::mutex> lock(dirname_lock);
-#endif
-
- // Note that if std::string uses copy-on-write strings, &str[0] will cause
- // the copy to be made, so there is no chance of us accidentally writing to
- // the storage for 'path'.
- char* parent = dirname(&result[0]);
-
- // In case dirname returned a pointer to a process global, copy that string
- // before leaving the lock.
- result.assign(parent);
-
- return result;
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/file_test.cpp b/base/file_test.cpp
deleted file mode 100644
index 120228d..0000000
--- a/base/file_test.cpp
+++ /dev/null
@@ -1,385 +0,0 @@
-/*
- * Copyright (C) 2013 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 "android-base/file.h"
-
-#include "android-base/utf8.h"
-
-#include <gtest/gtest.h>
-
-#include <errno.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <wchar.h>
-
-#include <string>
-
-#if !defined(_WIN32)
-#include <pwd.h>
-#else
-#include <windows.h>
-#endif
-
-#include "android-base/logging.h" // and must be after windows.h for ERROR
-
-TEST(file, ReadFileToString_ENOENT) {
- std::string s("hello");
- errno = 0;
- ASSERT_FALSE(android::base::ReadFileToString("/proc/does-not-exist", &s));
- EXPECT_EQ(ENOENT, errno);
- EXPECT_EQ("", s); // s was cleared.
-}
-
-TEST(file, ReadFileToString_WriteStringToFile) {
- TemporaryFile tf;
- ASSERT_NE(tf.fd, -1) << tf.path;
- ASSERT_TRUE(android::base::WriteStringToFile("abc", tf.path))
- << strerror(errno);
- std::string s;
- ASSERT_TRUE(android::base::ReadFileToString(tf.path, &s))
- << strerror(errno);
- EXPECT_EQ("abc", s);
-}
-
-// symlinks require elevated privileges on Windows.
-#if !defined(_WIN32)
-TEST(file, ReadFileToString_WriteStringToFile_symlink) {
- TemporaryFile target, link;
- ASSERT_EQ(0, unlink(link.path));
- ASSERT_EQ(0, symlink(target.path, link.path));
- ASSERT_FALSE(android::base::WriteStringToFile("foo", link.path, false));
- ASSERT_EQ(ELOOP, errno);
- ASSERT_TRUE(android::base::WriteStringToFile("foo", link.path, true));
-
- std::string s;
- ASSERT_FALSE(android::base::ReadFileToString(link.path, &s));
- ASSERT_EQ(ELOOP, errno);
- ASSERT_TRUE(android::base::ReadFileToString(link.path, &s, true));
- ASSERT_EQ("foo", s);
-}
-#endif
-
-// WriteStringToFile2 is explicitly for setting Unix permissions, which make no
-// sense on Windows.
-#if !defined(_WIN32)
-TEST(file, WriteStringToFile2) {
- TemporaryFile tf;
- ASSERT_NE(tf.fd, -1) << tf.path;
- ASSERT_TRUE(android::base::WriteStringToFile("abc", tf.path, 0660,
- getuid(), getgid()))
- << strerror(errno);
- struct stat sb;
- ASSERT_EQ(0, stat(tf.path, &sb));
- ASSERT_EQ(0660U, static_cast<unsigned int>(sb.st_mode & ~S_IFMT));
- ASSERT_EQ(getuid(), sb.st_uid);
- ASSERT_EQ(getgid(), sb.st_gid);
- std::string s;
- ASSERT_TRUE(android::base::ReadFileToString(tf.path, &s))
- << strerror(errno);
- EXPECT_EQ("abc", s);
-}
-#endif
-
-#if defined(_WIN32)
-TEST(file, NonUnicodeCharsWindows) {
- constexpr auto kMaxEnvVariableValueSize = 32767;
- std::wstring old_tmp;
- old_tmp.resize(kMaxEnvVariableValueSize);
- old_tmp.resize(GetEnvironmentVariableW(L"TMP", old_tmp.data(), old_tmp.size()));
- if (old_tmp.empty()) {
- // Can't continue with empty TMP folder.
- return;
- }
-
- std::wstring new_tmp = old_tmp;
- if (new_tmp.back() != L'\\') {
- new_tmp.push_back(L'\\');
- }
-
- {
- auto path(new_tmp + L"锦绣成都\\");
- _wmkdir(path.c_str());
- ASSERT_TRUE(SetEnvironmentVariableW(L"TMP", path.c_str()));
-
- TemporaryFile tf;
- ASSERT_NE(tf.fd, -1) << tf.path;
- ASSERT_TRUE(android::base::WriteStringToFd("abc", tf.fd));
-
- ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
-
- std::string s;
- ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s)) << strerror(errno);
- EXPECT_EQ("abc", s);
- }
- {
- auto path(new_tmp + L"директория с длинным именем\\");
- _wmkdir(path.c_str());
- ASSERT_TRUE(SetEnvironmentVariableW(L"TMP", path.c_str()));
-
- TemporaryFile tf;
- ASSERT_NE(tf.fd, -1) << tf.path;
- ASSERT_TRUE(android::base::WriteStringToFd("abc", tf.fd));
-
- ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
-
- std::string s;
- ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s)) << strerror(errno);
- EXPECT_EQ("abc", s);
- }
- {
- auto path(new_tmp + L"äüöß weiß\\");
- _wmkdir(path.c_str());
- ASSERT_TRUE(SetEnvironmentVariableW(L"TMP", path.c_str()));
-
- TemporaryFile tf;
- ASSERT_NE(tf.fd, -1) << tf.path;
- ASSERT_TRUE(android::base::WriteStringToFd("abc", tf.fd));
-
- ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
-
- std::string s;
- ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s)) << strerror(errno);
- EXPECT_EQ("abc", s);
- }
-
- SetEnvironmentVariableW(L"TMP", old_tmp.c_str());
-}
-
-TEST(file, RootDirectoryWindows) {
- constexpr auto kMaxEnvVariableValueSize = 32767;
- std::wstring old_tmp;
- bool tmp_is_empty = false;
- old_tmp.resize(kMaxEnvVariableValueSize);
- old_tmp.resize(GetEnvironmentVariableW(L"TMP", old_tmp.data(), old_tmp.size()));
- if (old_tmp.empty()) {
- tmp_is_empty = (GetLastError() == ERROR_ENVVAR_NOT_FOUND);
- }
- SetEnvironmentVariableW(L"TMP", L"C:");
-
- TemporaryFile tf;
- ASSERT_NE(tf.fd, -1) << tf.path;
-
- SetEnvironmentVariableW(L"TMP", tmp_is_empty ? nullptr : old_tmp.c_str());
-}
-#endif
-
-TEST(file, WriteStringToFd) {
- TemporaryFile tf;
- ASSERT_NE(tf.fd, -1) << tf.path;
- ASSERT_TRUE(android::base::WriteStringToFd("abc", tf.fd));
-
- ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
-
- std::string s;
- ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s)) << strerror(errno);
- EXPECT_EQ("abc", s);
-}
-
-TEST(file, WriteFully) {
- TemporaryFile tf;
- ASSERT_NE(tf.fd, -1) << tf.path;
- ASSERT_TRUE(android::base::WriteFully(tf.fd, "abc", 3));
-
- ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
-
- std::string s;
- s.resize(3);
- ASSERT_TRUE(android::base::ReadFully(tf.fd, &s[0], s.size()))
- << strerror(errno);
- EXPECT_EQ("abc", s);
-
- ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
-
- s.resize(1024);
- ASSERT_FALSE(android::base::ReadFully(tf.fd, &s[0], s.size()));
-}
-
-TEST(file, RemoveFileIfExists) {
- TemporaryFile tf;
- ASSERT_NE(tf.fd, -1) << tf.path;
- close(tf.fd);
- tf.fd = -1;
- std::string err;
- ASSERT_TRUE(android::base::RemoveFileIfExists(tf.path, &err)) << err;
- ASSERT_TRUE(android::base::RemoveFileIfExists(tf.path));
- TemporaryDir td;
- ASSERT_FALSE(android::base::RemoveFileIfExists(td.path));
- ASSERT_FALSE(android::base::RemoveFileIfExists(td.path, &err));
- ASSERT_EQ("is not a regular file or symbolic link", err);
-}
-
-TEST(file, RemoveFileIfExists_ENOTDIR) {
- TemporaryFile tf;
- close(tf.fd);
- tf.fd = -1;
- std::string err{"xxx"};
- ASSERT_TRUE(android::base::RemoveFileIfExists(std::string{tf.path} + "/abc", &err));
- ASSERT_EQ("xxx", err);
-}
-
-#if !defined(_WIN32)
-TEST(file, RemoveFileIfExists_EACCES) {
- // EACCES -- one of the directories in the path has no search permission
- // root can bypass permission restrictions, so drop root.
- if (getuid() == 0) {
- passwd* shell = getpwnam("shell");
- setgid(shell->pw_gid);
- setuid(shell->pw_uid);
- }
-
- TemporaryDir td;
- TemporaryFile tf(td.path);
- close(tf.fd);
- tf.fd = -1;
- std::string err{"xxx"};
- // Remove dir's search permission.
- ASSERT_TRUE(chmod(td.path, S_IRUSR | S_IWUSR) == 0);
- ASSERT_FALSE(android::base::RemoveFileIfExists(tf.path, &err));
- ASSERT_EQ("Permission denied", err);
- // Set dir's search permission again.
- ASSERT_TRUE(chmod(td.path, S_IRWXU) == 0);
- ASSERT_TRUE(android::base::RemoveFileIfExists(tf.path, &err));
-}
-#endif
-
-TEST(file, Readlink) {
-#if !defined(_WIN32)
- // Linux doesn't allow empty symbolic links.
- std::string min("x");
- // ext2 and ext4 both have PAGE_SIZE limits.
- // If file encryption is enabled, there's extra overhead to store the
- // size of the encrypted symlink target. There's also an off-by-one
- // in current kernels (and marlin/sailfish where we're seeing this
- // failure are still on 3.18, far from current). http://b/33306057.
- std::string max(static_cast<size_t>(4096 - 2 - 1 - 1), 'x');
-
- TemporaryDir td;
- std::string min_path{std::string(td.path) + "/" + "min"};
- std::string max_path{std::string(td.path) + "/" + "max"};
-
- ASSERT_EQ(0, symlink(min.c_str(), min_path.c_str()));
- ASSERT_EQ(0, symlink(max.c_str(), max_path.c_str()));
-
- std::string result;
-
- result = "wrong";
- ASSERT_TRUE(android::base::Readlink(min_path, &result));
- ASSERT_EQ(min, result);
-
- result = "wrong";
- ASSERT_TRUE(android::base::Readlink(max_path, &result));
- ASSERT_EQ(max, result);
-#endif
-}
-
-TEST(file, Realpath) {
-#if !defined(_WIN32)
- TemporaryDir td;
- std::string basename = android::base::Basename(td.path);
- std::string dir_name = android::base::Dirname(td.path);
- std::string base_dir_name = android::base::Basename(dir_name);
-
- {
- std::string path = dir_name + "/../" + base_dir_name + "/" + basename;
- std::string result;
- ASSERT_TRUE(android::base::Realpath(path, &result));
- ASSERT_EQ(td.path, result);
- }
-
- {
- std::string path = std::string(td.path) + "/..";
- std::string result;
- ASSERT_TRUE(android::base::Realpath(path, &result));
- ASSERT_EQ(dir_name, result);
- }
-
- {
- errno = 0;
- std::string path = std::string(td.path) + "/foo.noent";
- std::string result = "wrong";
- ASSERT_TRUE(!android::base::Realpath(path, &result));
- ASSERT_TRUE(result.empty());
- ASSERT_EQ(ENOENT, errno);
- }
-#endif
-}
-
-TEST(file, GetExecutableDirectory) {
- std::string path = android::base::GetExecutableDirectory();
- ASSERT_NE("", path);
- ASSERT_NE(android::base::GetExecutablePath(), path);
- ASSERT_EQ('/', path[0]);
- ASSERT_NE('/', path[path.size() - 1]);
-}
-
-TEST(file, GetExecutablePath) {
- ASSERT_NE("", android::base::GetExecutablePath());
-}
-
-TEST(file, Basename) {
- EXPECT_EQ("sh", android::base::Basename("/system/bin/sh"));
- EXPECT_EQ("sh", android::base::Basename("sh"));
- EXPECT_EQ("sh", android::base::Basename("/system/bin/sh/"));
-}
-
-TEST(file, Dirname) {
- EXPECT_EQ("/system/bin", android::base::Dirname("/system/bin/sh"));
- EXPECT_EQ(".", android::base::Dirname("sh"));
- EXPECT_EQ("/system/bin", android::base::Dirname("/system/bin/sh/"));
-}
-
-TEST(file, ReadFileToString_capacity) {
- TemporaryFile tf;
- ASSERT_NE(tf.fd, -1) << tf.path;
-
- // For a huge file, the overhead should still be small.
- std::string s;
- size_t size = 16 * 1024 * 1024;
- ASSERT_TRUE(android::base::WriteStringToFile(std::string(size, 'x'), tf.path));
- ASSERT_TRUE(android::base::ReadFileToString(tf.path, &s));
- EXPECT_EQ(size, s.size());
- EXPECT_LT(s.capacity(), size + 16);
-
- // Even for weird badly-aligned sizes.
- size += 12345;
- ASSERT_TRUE(android::base::WriteStringToFile(std::string(size, 'x'), tf.path));
- ASSERT_TRUE(android::base::ReadFileToString(tf.path, &s));
- EXPECT_EQ(size, s.size());
- EXPECT_LT(s.capacity(), size + 16);
-
- // We'll shrink an enormous string if you read a small file into it.
- size = 64;
- ASSERT_TRUE(android::base::WriteStringToFile(std::string(size, 'x'), tf.path));
- ASSERT_TRUE(android::base::ReadFileToString(tf.path, &s));
- EXPECT_EQ(size, s.size());
- EXPECT_LT(s.capacity(), size + 16);
-}
-
-TEST(file, ReadFileToString_capacity_0) {
- TemporaryFile tf;
- ASSERT_NE(tf.fd, -1) << tf.path;
-
- // Because /proc reports its files as zero-length, we don't actually trust
- // any file that claims to be zero-length. Rather than add increasingly
- // complex heuristics for shrinking the passed-in string in that case, we
- // currently leave it alone.
- std::string s;
- size_t initial_capacity = s.capacity();
- ASSERT_TRUE(android::base::WriteStringToFile("", tf.path));
- ASSERT_TRUE(android::base::ReadFileToString(tf.path, &s));
- EXPECT_EQ(0U, s.size());
- EXPECT_EQ(initial_capacity, s.capacity());
-}
diff --git a/base/format_benchmark.cpp b/base/format_benchmark.cpp
deleted file mode 100644
index 9590b23..0000000
--- a/base/format_benchmark.cpp
+++ /dev/null
@@ -1,80 +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 "android-base/format.h"
-
-#include <limits>
-
-#include <benchmark/benchmark.h>
-
-#include "android-base/stringprintf.h"
-
-using android::base::StringPrintf;
-
-static void BenchmarkFormatInt(benchmark::State& state) {
- for (auto _ : state) {
- benchmark::DoNotOptimize(fmt::format("{} {} {}", 42, std::numeric_limits<int>::min(),
- std::numeric_limits<int>::max()));
- }
-}
-
-BENCHMARK(BenchmarkFormatInt);
-
-static void BenchmarkStringPrintfInt(benchmark::State& state) {
- for (auto _ : state) {
- benchmark::DoNotOptimize(StringPrintf("%d %d %d", 42, std::numeric_limits<int>::min(),
- std::numeric_limits<int>::max()));
- }
-}
-
-BENCHMARK(BenchmarkStringPrintfInt);
-
-static void BenchmarkFormatFloat(benchmark::State& state) {
- for (auto _ : state) {
- benchmark::DoNotOptimize(fmt::format("{} {} {}", 42.42, std::numeric_limits<float>::min(),
- std::numeric_limits<float>::max()));
- }
-}
-
-BENCHMARK(BenchmarkFormatFloat);
-
-static void BenchmarkStringPrintfFloat(benchmark::State& state) {
- for (auto _ : state) {
- benchmark::DoNotOptimize(StringPrintf("%f %f %f", 42.42, std::numeric_limits<float>::min(),
- std::numeric_limits<float>::max()));
- }
-}
-
-BENCHMARK(BenchmarkStringPrintfFloat);
-
-static void BenchmarkFormatStrings(benchmark::State& state) {
- for (auto _ : state) {
- benchmark::DoNotOptimize(fmt::format("{} hello there {}", "hi,", "!!"));
- }
-}
-
-BENCHMARK(BenchmarkFormatStrings);
-
-static void BenchmarkStringPrintfStrings(benchmark::State& state) {
- for (auto _ : state) {
- benchmark::DoNotOptimize(StringPrintf("%s hello there %s", "hi,", "!!"));
- }
-}
-
-BENCHMARK(BenchmarkStringPrintfStrings);
-
-// Run the benchmark
-BENCHMARK_MAIN();
diff --git a/base/include/android-base/chrono_utils.h b/base/include/android-base/chrono_utils.h
deleted file mode 100644
index 11fcf71..0000000
--- a/base/include/android-base/chrono_utils.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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
-
-#include <chrono>
-#include <sstream>
-
-#if __cplusplus > 201103L && !defined(__WIN32) // C++14
-using namespace std::chrono_literals;
-#endif
-
-namespace android {
-namespace base {
-
-// A std::chrono clock based on CLOCK_BOOTTIME.
-class boot_clock {
- public:
- typedef std::chrono::nanoseconds duration;
- typedef std::chrono::time_point<boot_clock, duration> time_point;
-
- static time_point now();
-};
-
-class Timer {
- public:
- Timer() : start_(boot_clock::now()) {}
-
- std::chrono::milliseconds duration() const {
- return std::chrono::duration_cast<std::chrono::milliseconds>(boot_clock::now() - start_);
- }
-
- private:
- boot_clock::time_point start_;
-};
-
-std::ostream& operator<<(std::ostream& os, const Timer& t);
-
-} // namespace base
-} // namespace android
diff --git a/base/include/android-base/cmsg.h b/base/include/android-base/cmsg.h
deleted file mode 100644
index e4197b1..0000000
--- a/base/include/android-base/cmsg.h
+++ /dev/null
@@ -1,106 +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 <sys/stat.h>
-#include <sys/types.h>
-
-#include <type_traits>
-#include <vector>
-
-#include <android-base/collections.h>
-#include <android-base/macros.h>
-#include <android-base/unique_fd.h>
-
-namespace android {
-namespace base {
-
-#if !defined(_WIN32)
-
-// Helpers for sending and receiving file descriptors across Unix domain sockets.
-//
-// The cmsg(3) API is very hard to get right, with multiple landmines that can
-// lead to death. Almost all of the uses of cmsg in Android make at least one of
-// the following mistakes:
-//
-// - not aligning the cmsg buffer
-// - leaking fds if more fds are received than expected
-// - blindly dereferencing CMSG_DATA without checking the header
-// - using CMSG_SPACE instead of CMSG_LEN for .cmsg_len
-// - using CMSG_LEN instead of CMSG_SPACE for .msg_controllen
-// - using a length specified in number of fds instead of bytes
-//
-// These functions wrap the hard-to-use cmsg API with an easier to use abstraction.
-
-// Send file descriptors across a Unix domain socket.
-//
-// Note that the write can return short if the socket type is SOCK_STREAM. When
-// this happens, file descriptors are still sent to the other end, but with
-// truncated data. For this reason, using SOCK_SEQPACKET or SOCK_DGRAM is recommended.
-ssize_t SendFileDescriptorVector(borrowed_fd sock, const void* data, size_t len,
- const std::vector<int>& fds);
-
-// Receive file descriptors from a Unix domain socket.
-//
-// If more FDs (or bytes, for datagram sockets) are received than expected,
-// -1 is returned with errno set to EMSGSIZE, and all received FDs are thrown away.
-ssize_t ReceiveFileDescriptorVector(borrowed_fd sock, void* data, size_t len, size_t max_fds,
- std::vector<android::base::unique_fd>* fds);
-
-// Helper for SendFileDescriptorVector that constructs a std::vector for you, e.g.:
-// SendFileDescriptors(sock, "foo", 3, std::move(fd1), std::move(fd2))
-template <typename... Args>
-ssize_t SendFileDescriptors(borrowed_fd sock, const void* data, size_t len, Args&&... sent_fds) {
- // Do not allow implicit conversion to int: people might try to do something along the lines of:
- // SendFileDescriptors(..., std::move(a_unique_fd))
- // and be surprised when the unique_fd isn't closed afterwards.
- AssertType<int>(std::forward<Args>(sent_fds)...);
- std::vector<int> fds;
- Append(fds, std::forward<Args>(sent_fds)...);
- return SendFileDescriptorVector(sock, data, len, fds);
-}
-
-// Helper for ReceiveFileDescriptorVector that receives an exact number of file descriptors.
-// If more file descriptors are received than requested, -1 is returned with errno set to EMSGSIZE.
-// If fewer file descriptors are received than requested, -1 is returned with errno set to ENOMSG.
-// In both cases, all arguments are cleared and any received FDs are thrown away.
-template <typename... Args>
-ssize_t ReceiveFileDescriptors(borrowed_fd sock, void* data, size_t len, Args&&... received_fds) {
- std::vector<unique_fd*> fds;
- Append(fds, std::forward<Args>(received_fds)...);
-
- std::vector<unique_fd> result;
- ssize_t rc = ReceiveFileDescriptorVector(sock, data, len, fds.size(), &result);
- if (rc == -1 || result.size() != fds.size()) {
- int err = rc == -1 ? errno : ENOMSG;
- for (unique_fd* fd : fds) {
- fd->reset();
- }
- errno = err;
- return -1;
- }
-
- for (size_t i = 0; i < fds.size(); ++i) {
- *fds[i] = std::move(result[i]);
- }
- return rc;
-}
-
-#endif
-
-} // namespace base
-} // namespace android
diff --git a/base/include/android-base/collections.h b/base/include/android-base/collections.h
deleted file mode 100644
index be0683a..0000000
--- a/base/include/android-base/collections.h
+++ /dev/null
@@ -1,60 +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 <utility>
-
-namespace android {
-namespace base {
-
-// Helpers for converting a variadic template parameter pack to a homogeneous collection.
-// Parameters must be implictly convertible to the contained type (including via move/copy ctors).
-//
-// Use as follows:
-//
-// template <typename... Args>
-// std::vector<int> CreateVector(Args&&... args) {
-// std::vector<int> result;
-// Append(result, std::forward<Args>(args)...);
-// return result;
-// }
-template <typename CollectionType, typename T>
-void Append(CollectionType& collection, T&& arg) {
- collection.push_back(std::forward<T>(arg));
-}
-
-template <typename CollectionType, typename T, typename... Args>
-void Append(CollectionType& collection, T&& arg, Args&&... args) {
- collection.push_back(std::forward<T>(arg));
- return Append(collection, std::forward<Args>(args)...);
-}
-
-// Assert that all of the arguments in a variadic template parameter pack are of a given type
-// after std::decay.
-template <typename T, typename Arg, typename... Args>
-void AssertType(Arg&&) {
- static_assert(std::is_same<T, typename std::decay<Arg>::type>::value);
-}
-
-template <typename T, typename Arg, typename... Args>
-void AssertType(Arg&&, Args&&... args) {
- static_assert(std::is_same<T, typename std::decay<Arg>::type>::value);
- AssertType<T>(std::forward<Args>(args)...);
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/include/android-base/endian.h b/base/include/android-base/endian.h
deleted file mode 100644
index 8fa6365..0000000
--- a/base/include/android-base/endian.h
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * 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
-
-/* A cross-platform equivalent of bionic's <sys/endian.h>. */
-
-/* For __BIONIC__ and __GLIBC__ */
-#include <sys/cdefs.h>
-
-#if defined(__BIONIC__)
-
-#include <sys/endian.h>
-
-#elif defined(__GLIBC__)
-
-/* glibc's <endian.h> is like bionic's <sys/endian.h>. */
-#include <endian.h>
-
-/* glibc keeps htons and htonl in <netinet/in.h>. */
-#include <netinet/in.h>
-
-/* glibc doesn't have the 64-bit variants. */
-#define htonq(x) htobe64(x)
-#define ntohq(x) be64toh(x)
-
-/* glibc has different names to BSD for these. */
-#define betoh16(x) be16toh(x)
-#define betoh32(x) be32toh(x)
-#define betoh64(x) be64toh(x)
-#define letoh16(x) le16toh(x)
-#define letoh32(x) le32toh(x)
-#define letoh64(x) le64toh(x)
-
-#else
-
-#if defined(__APPLE__)
-/* macOS has some of the basics. */
-#include <sys/_endian.h>
-#else
-/* Windows has some of the basics as well. */
-#include <sys/param.h>
-#include <winsock2.h>
-/* winsock2.h *must* be included before the following four macros. */
-#define htons(x) __builtin_bswap16(x)
-#define htonl(x) __builtin_bswap32(x)
-#define ntohs(x) __builtin_bswap16(x)
-#define ntohl(x) __builtin_bswap32(x)
-#endif
-
-/* Neither macOS nor Windows have the rest. */
-
-#define __LITTLE_ENDIAN 1234
-#define __BIG_ENDIAN 4321
-#define __BYTE_ORDER __LITTLE_ENDIAN
-
-#define htonq(x) __builtin_bswap64(x)
-
-#define ntohq(x) __builtin_bswap64(x)
-
-#define htobe16(x) __builtin_bswap16(x)
-#define htobe32(x) __builtin_bswap32(x)
-#define htobe64(x) __builtin_bswap64(x)
-
-#define betoh16(x) __builtin_bswap16(x)
-#define betoh32(x) __builtin_bswap32(x)
-#define betoh64(x) __builtin_bswap64(x)
-
-#define htole16(x) (x)
-#define htole32(x) (x)
-#define htole64(x) (x)
-
-#define letoh16(x) (x)
-#define letoh32(x) (x)
-#define letoh64(x) (x)
-
-#define be16toh(x) __builtin_bswap16(x)
-#define be32toh(x) __builtin_bswap32(x)
-#define be64toh(x) __builtin_bswap64(x)
-
-#define le16toh(x) (x)
-#define le32toh(x) (x)
-#define le64toh(x) (x)
-
-#endif
diff --git a/base/include/android-base/errno_restorer.h b/base/include/android-base/errno_restorer.h
deleted file mode 100644
index 1c8597c..0000000
--- a/base/include/android-base/errno_restorer.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "errno.h"
-
-#include "android-base/macros.h"
-
-namespace android {
-namespace base {
-
-class ErrnoRestorer {
- public:
- ErrnoRestorer() : saved_errno_(errno) {}
-
- ~ErrnoRestorer() { errno = saved_errno_; }
-
- // Allow this object to be used as part of && operation.
- operator bool() const { return true; }
-
- private:
- const int saved_errno_;
-
- DISALLOW_COPY_AND_ASSIGN(ErrnoRestorer);
-};
-
-} // namespace base
-} // namespace android
diff --git a/base/include/android-base/errors.h b/base/include/android-base/errors.h
deleted file mode 100644
index 06f29fc..0000000
--- a/base/include/android-base/errors.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// Portable error handling functions. This is only necessary for host-side
-// code that needs to be cross-platform; code that is only run on Unix should
-// just use errno and strerror() for simplicity.
-//
-// There is some complexity since Windows has (at least) three different error
-// numbers, not all of which share the same type:
-// * errno: for C runtime errors.
-// * GetLastError(): Windows non-socket errors.
-// * WSAGetLastError(): Windows socket errors.
-// errno can be passed to strerror() on all platforms, but the other two require
-// special handling to get the error string. Refer to Microsoft documentation
-// to determine which error code to check for each function.
-
-#pragma once
-
-#include <string>
-
-namespace android {
-namespace base {
-
-// Returns a string describing the given system error code. |error_code| must
-// be errno on Unix or GetLastError()/WSAGetLastError() on Windows. Passing
-// errno on Windows has undefined behavior.
-std::string SystemErrorCodeToString(int error_code);
-
-} // namespace base
-} // namespace android
diff --git a/base/include/android-base/expected.h b/base/include/android-base/expected.h
deleted file mode 100644
index 9603bb1..0000000
--- a/base/include/android-base/expected.h
+++ /dev/null
@@ -1,759 +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 <algorithm>
-#include <initializer_list>
-#include <type_traits>
-#include <utility>
-#include <variant>
-
-// android::base::expected is an Android implementation of the std::expected
-// proposal.
-// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0323r7.html
-//
-// Usage:
-// using android::base::expected;
-// using android::base::unexpected;
-//
-// expected<double,std::string> safe_divide(double i, double j) {
-// if (j == 0) return unexpected("divide by zero");
-// else return i / j;
-// }
-//
-// void test() {
-// auto q = safe_divide(10, 0);
-// if (q) { printf("%f\n", q.value()); }
-// else { printf("%s\n", q.error().c_str()); }
-// }
-//
-// When the proposal becomes part of the standard and is implemented by
-// libcxx, this will be removed and android::base::expected will be
-// type alias to std::expected.
-//
-
-namespace android {
-namespace base {
-
-// Synopsis
-template<class T, class E>
- class expected;
-
-template<class E>
- class unexpected;
-template<class E>
- unexpected(E) -> unexpected<E>;
-
-template<class E>
- class bad_expected_access;
-
-template<>
- class bad_expected_access<void>;
-
-struct unexpect_t {
- explicit unexpect_t() = default;
-};
-inline constexpr unexpect_t unexpect{};
-
-// macros for SFINAE
-#define _ENABLE_IF(...) \
- , std::enable_if_t<(__VA_ARGS__)>* = nullptr
-
-// Define NODISCARD_EXPECTED to prevent expected<T,E> from being
-// ignored when used as a return value. This is off by default.
-#ifdef NODISCARD_EXPECTED
-#define _NODISCARD_ [[nodiscard]]
-#else
-#define _NODISCARD_
-#endif
-
-// Class expected
-template<class T, class E>
-class _NODISCARD_ expected {
- public:
- using value_type = T;
- using error_type = E;
- using unexpected_type = unexpected<E>;
-
- template<class U>
- using rebind = expected<U, error_type>;
-
- // constructors
- constexpr expected() = default;
- constexpr expected(const expected& rhs) = default;
- constexpr expected(expected&& rhs) noexcept = default;
-
- template<class U, class G _ENABLE_IF(
- std::is_constructible_v<T, const U&> &&
- std::is_constructible_v<E, const G&> &&
- !std::is_constructible_v<T, expected<U, G>&> &&
- !std::is_constructible_v<T, expected<U, G>&&> &&
- !std::is_constructible_v<T, const expected<U, G>&> &&
- !std::is_constructible_v<T, const expected<U, G>&&> &&
- !std::is_convertible_v<expected<U, G>&, T> &&
- !std::is_convertible_v<expected<U, G>&&, T> &&
- !std::is_convertible_v<const expected<U, G>&, T> &&
- !std::is_convertible_v<const expected<U, G>&&, T> &&
- !(!std::is_convertible_v<const U&, T> ||
- !std::is_convertible_v<const G&, E>) /* non-explicit */
- )>
- // NOLINTNEXTLINE(google-explicit-constructor)
- constexpr expected(const expected<U, G>& rhs) {
- if (rhs.has_value()) var_ = rhs.value();
- else var_ = unexpected(rhs.error());
- }
-
- template<class U, class G _ENABLE_IF(
- std::is_constructible_v<T, const U&> &&
- std::is_constructible_v<E, const G&> &&
- !std::is_constructible_v<T, expected<U, G>&> &&
- !std::is_constructible_v<T, expected<U, G>&&> &&
- !std::is_constructible_v<T, const expected<U, G>&> &&
- !std::is_constructible_v<T, const expected<U, G>&&> &&
- !std::is_convertible_v<expected<U, G>&, T> &&
- !std::is_convertible_v<expected<U, G>&&, T> &&
- !std::is_convertible_v<const expected<U, G>&, T> &&
- !std::is_convertible_v<const expected<U, G>&&, T> &&
- (!std::is_convertible_v<const U&, T> ||
- !std::is_convertible_v<const G&, E>) /* explicit */
- )>
- constexpr explicit expected(const expected<U, G>& rhs) {
- if (rhs.has_value()) var_ = rhs.value();
- else var_ = unexpected(rhs.error());
- }
-
- template<class U, class G _ENABLE_IF(
- std::is_constructible_v<T, const U&> &&
- std::is_constructible_v<E, const G&> &&
- !std::is_constructible_v<T, expected<U, G>&> &&
- !std::is_constructible_v<T, expected<U, G>&&> &&
- !std::is_constructible_v<T, const expected<U, G>&> &&
- !std::is_constructible_v<T, const expected<U, G>&&> &&
- !std::is_convertible_v<expected<U, G>&, T> &&
- !std::is_convertible_v<expected<U, G>&&, T> &&
- !std::is_convertible_v<const expected<U, G>&, T> &&
- !std::is_convertible_v<const expected<U, G>&&, T> &&
- !(!std::is_convertible_v<const U&, T> ||
- !std::is_convertible_v<const G&, E>) /* non-explicit */
- )>
- // NOLINTNEXTLINE(google-explicit-constructor)
- constexpr expected(expected<U, G>&& rhs) {
- if (rhs.has_value()) var_ = std::move(rhs.value());
- else var_ = unexpected(std::move(rhs.error()));
- }
-
- template<class U, class G _ENABLE_IF(
- std::is_constructible_v<T, const U&> &&
- std::is_constructible_v<E, const G&> &&
- !std::is_constructible_v<T, expected<U, G>&> &&
- !std::is_constructible_v<T, expected<U, G>&&> &&
- !std::is_constructible_v<T, const expected<U, G>&> &&
- !std::is_constructible_v<T, const expected<U, G>&&> &&
- !std::is_convertible_v<expected<U, G>&, T> &&
- !std::is_convertible_v<expected<U, G>&&, T> &&
- !std::is_convertible_v<const expected<U, G>&, T> &&
- !std::is_convertible_v<const expected<U, G>&&, T> &&
- (!std::is_convertible_v<const U&, T> ||
- !std::is_convertible_v<const G&, E>) /* explicit */
- )>
- constexpr explicit expected(expected<U, G>&& rhs) {
- if (rhs.has_value()) var_ = std::move(rhs.value());
- else var_ = unexpected(std::move(rhs.error()));
- }
-
- template <class U = T _ENABLE_IF(
- std::is_constructible_v<T, U&&> &&
- !std::is_same_v<std::remove_cv_t<std::remove_reference_t<U>>, std::in_place_t> &&
- !std::is_same_v<expected<T, E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
- !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)
- constexpr expected(U&& v) : var_(std::in_place_index<0>, std::forward<U>(v)) {}
-
- template <class U = T _ENABLE_IF(
- std::is_constructible_v<T, U&&> &&
- !std::is_same_v<std::remove_cv_t<std::remove_reference_t<U>>, std::in_place_t> &&
- !std::is_same_v<expected<T, E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
- !std::is_same_v<unexpected<E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
- !std::is_convertible_v<U&&, T> /* explicit */
- )>
- constexpr explicit expected(U&& v) : var_(std::in_place_index<0>, T(std::forward<U>(v))) {}
-
- template<class G = E _ENABLE_IF(
- std::is_constructible_v<E, const G&> &&
- std::is_convertible_v<const G&, E> /* non-explicit */
- )>
- // NOLINTNEXTLINE(google-explicit-constructor)
- constexpr expected(const unexpected<G>& e)
- : var_(std::in_place_index<1>, e.value()) {}
-
- template<class G = E _ENABLE_IF(
- std::is_constructible_v<E, const G&> &&
- !std::is_convertible_v<const G&, E> /* explicit */
- )>
- constexpr explicit expected(const unexpected<G>& e)
- : var_(std::in_place_index<1>, E(e.value())) {}
-
- template<class G = E _ENABLE_IF(
- std::is_constructible_v<E, G&&> &&
- std::is_convertible_v<G&&, E> /* non-explicit */
- )>
- // NOLINTNEXTLINE(google-explicit-constructor)
- constexpr expected(unexpected<G>&& e)
- : var_(std::in_place_index<1>, std::move(e.value())) {}
-
- template<class G = E _ENABLE_IF(
- std::is_constructible_v<E, G&&> &&
- !std::is_convertible_v<G&&, E> /* explicit */
- )>
- constexpr explicit expected(unexpected<G>&& e)
- : var_(std::in_place_index<1>, E(std::move(e.value()))) {}
-
- template<class... Args _ENABLE_IF(
- std::is_constructible_v<T, Args&&...>
- )>
- constexpr explicit expected(std::in_place_t, Args&&... args)
- : var_(std::in_place_index<0>, std::forward<Args>(args)...) {}
-
- template<class U, class... Args _ENABLE_IF(
- std::is_constructible_v<T, std::initializer_list<U>&, Args...>
- )>
- constexpr explicit expected(std::in_place_t, std::initializer_list<U> il, Args&&... args)
- : var_(std::in_place_index<0>, il, std::forward<Args>(args)...) {}
-
- template<class... Args _ENABLE_IF(
- std::is_constructible_v<E, Args...>
- )>
- constexpr explicit expected(unexpect_t, Args&&... args)
- : var_(unexpected_type(std::forward<Args>(args)...)) {}
-
- template<class U, class... Args _ENABLE_IF(
- std::is_constructible_v<E, std::initializer_list<U>&, Args...>
- )>
- constexpr explicit expected(unexpect_t, std::initializer_list<U> il, Args&&... args)
- : var_(unexpected_type(il, std::forward<Args>(args)...)) {}
-
- // destructor
- ~expected() = default;
-
- // assignment
- // Note: SFNAIE doesn't work here because assignment operator should be
- // non-template. We could workaround this by defining a templated parent class
- // having the assignment operator. This incomplete implementation however
- // doesn't allow us to copy assign expected<T,E> even when T is non-copy
- // assignable. The copy assignment will fail by the underlying std::variant
- // anyway though the error message won't be clear.
- expected& operator=(const expected& rhs) = default;
-
- // Note for SFNAIE above applies to here as well
- expected& operator=(expected&& rhs) noexcept(
- std::is_nothrow_move_assignable_v<T>&& std::is_nothrow_move_assignable_v<E>) = default;
-
- template <class U = T _ENABLE_IF(
- !std::is_void_v<T> &&
- !std::is_same_v<expected<T, E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
- !std::conjunction_v<std::is_scalar<T>, std::is_same<T, std::decay_t<U>>> &&
- std::is_constructible_v<T, U> && std::is_assignable_v<T&, U> &&
- std::is_nothrow_move_constructible_v<E>)>
- expected& operator=(U&& rhs) {
- var_ = T(std::forward<U>(rhs));
- return *this;
- }
-
- template<class G = E>
- expected& operator=(const unexpected<G>& rhs) {
- var_ = rhs;
- return *this;
- }
-
- template<class G = E _ENABLE_IF(
- std::is_nothrow_move_constructible_v<G> &&
- std::is_move_assignable_v<G>
- )>
- expected& operator=(unexpected<G>&& rhs) {
- var_ = std::move(rhs);
- return *this;
- }
-
- // modifiers
- template<class... Args _ENABLE_IF(
- std::is_nothrow_constructible_v<T, Args...>
- )>
- T& emplace(Args&&... args) {
- expected(std::in_place, std::forward<Args>(args)...).swap(*this);
- return value();
- }
-
- template<class U, class... Args _ENABLE_IF(
- std::is_nothrow_constructible_v<T, std::initializer_list<U>&, Args...>
- )>
- T& emplace(std::initializer_list<U> il, Args&&... args) {
- expected(std::in_place, il, std::forward<Args>(args)...).swap(*this);
- return value();
- }
-
- // swap
- template<typename U = T, typename = std::enable_if_t<(
- std::is_swappable_v<U> &&
- std::is_swappable_v<E> &&
- (std::is_move_constructible_v<U> ||
- std::is_move_constructible_v<E>))>>
- void swap(expected& rhs) noexcept(
- std::is_nothrow_move_constructible_v<T> &&
- std::is_nothrow_swappable_v<T> &&
- std::is_nothrow_move_constructible_v<E> &&
- std::is_nothrow_swappable_v<E>) {
- var_.swap(rhs.var_);
- }
-
- // observers
- constexpr const T* operator->() const { return std::addressof(value()); }
- constexpr T* operator->() { return std::addressof(value()); }
- constexpr const T& operator*() const& { return value(); }
- constexpr T& operator*() & { return value(); }
- constexpr const T&& operator*() const&& { return std::move(std::get<T>(var_)); }
- constexpr T&& operator*() && { return std::move(std::get<T>(var_)); }
-
- constexpr explicit operator bool() const noexcept { return has_value(); }
- constexpr bool has_value() const noexcept { return var_.index() == 0; }
- constexpr bool ok() const noexcept { return has_value(); }
-
- constexpr const T& value() const& { return std::get<T>(var_); }
- constexpr T& value() & { return std::get<T>(var_); }
- constexpr const T&& value() const&& { return std::move(std::get<T>(var_)); }
- constexpr T&& value() && { return std::move(std::get<T>(var_)); }
-
- constexpr const E& error() const& { return std::get<unexpected_type>(var_).value(); }
- constexpr E& error() & { return std::get<unexpected_type>(var_).value(); }
- constexpr const E&& error() const&& { return std::move(std::get<unexpected_type>(var_)).value(); }
- constexpr E&& error() && { return std::move(std::get<unexpected_type>(var_)).value(); }
-
- template<class U _ENABLE_IF(
- std::is_copy_constructible_v<T> &&
- std::is_convertible_v<U, T>
- )>
- constexpr T value_or(U&& v) const& {
- if (has_value()) return value();
- else return static_cast<T>(std::forward<U>(v));
- }
-
- template<class U _ENABLE_IF(
- std::is_move_constructible_v<T> &&
- std::is_convertible_v<U, T>
- )>
- constexpr T value_or(U&& v) && {
- if (has_value()) return std::move(value());
- else return static_cast<T>(std::forward<U>(v));
- }
-
- // expected equality operators
- template<class T1, class E1, class T2, class E2>
- friend constexpr bool operator==(const expected<T1, E1>& x, const expected<T2, E2>& y);
- template<class T1, class E1, class T2, class E2>
- friend constexpr bool operator!=(const expected<T1, E1>& x, const expected<T2, E2>& y);
-
- // Comparison with unexpected<E>
- template<class T1, class E1, class E2>
- friend constexpr bool operator==(const expected<T1, E1>&, const unexpected<E2>&);
- template<class T1, class E1, class E2>
- friend constexpr bool operator==(const unexpected<E2>&, const expected<T1, E1>&);
- template<class T1, class E1, class E2>
- friend constexpr bool operator!=(const expected<T1, E1>&, const unexpected<E2>&);
- template<class T1, class E1, class E2>
- friend constexpr bool operator!=(const unexpected<E2>&, const expected<T1, E1>&);
-
- // Specialized algorithms
- template<class T1, class E1>
- friend void swap(expected<T1, E1>&, expected<T1, E1>&) noexcept;
-
- private:
- std::variant<value_type, unexpected_type> var_;
-};
-
-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;
- }
-}
-
-template<class T1, class E1, class T2, class E2>
-constexpr bool operator!=(const expected<T1, E1>& x, const expected<T2, E2>& y) {
- return !(x == y);
-}
-
-// Comparison with unexpected<E>
-template<class T1, class E1, class E2>
-constexpr bool operator==(const expected<T1, E1>& x, const unexpected<E2>& y) {
- return !x.has_value() && (x.error() == y.value());
-}
-template<class T1, class E1, class E2>
-constexpr bool operator==(const unexpected<E2>& x, const expected<T1, E1>& y) {
- return !y.has_value() && (x.value() == y.error());
-}
-template<class T1, class E1, class E2>
-constexpr bool operator!=(const expected<T1, E1>& x, const unexpected<E2>& y) {
- return x.has_value() || (x.error() != y.value());
-}
-template<class T1, class E1, class E2>
-constexpr bool operator!=(const unexpected<E2>& x, const expected<T1, E1>& y) {
- return y.has_value() || (x.value() != y.error());
-}
-
-template<class E>
-class _NODISCARD_ expected<void, E> {
- public:
- using value_type = void;
- using error_type = E;
- using unexpected_type = unexpected<E>;
-
- // constructors
- constexpr expected() = default;
- constexpr expected(const expected& rhs) = default;
- constexpr expected(expected&& rhs) noexcept = default;
-
- template<class U, class G _ENABLE_IF(
- std::is_void_v<U> &&
- std::is_convertible_v<const G&, E> /* non-explicit */
- )>
- // NOLINTNEXTLINE(google-explicit-constructor)
- constexpr expected(const expected<U, G>& rhs) {
- if (!rhs.has_value()) var_ = unexpected(rhs.error());
- }
-
- template<class U, class G _ENABLE_IF(
- std::is_void_v<U> &&
- !std::is_convertible_v<const G&, E> /* explicit */
- )>
- constexpr explicit expected(const expected<U, G>& rhs) {
- if (!rhs.has_value()) var_ = unexpected(rhs.error());
- }
-
- template<class U, class G _ENABLE_IF(
- std::is_void_v<U> &&
- std::is_convertible_v<const G&&, E> /* non-explicit */
- )>
- // NOLINTNEXTLINE(google-explicit-constructor)
- constexpr expected(expected<U, G>&& rhs) {
- if (!rhs.has_value()) var_ = unexpected(std::move(rhs.error()));
- }
-
- template<class U, class G _ENABLE_IF(
- std::is_void_v<U> &&
- !std::is_convertible_v<const G&&, E> /* explicit */
- )>
- constexpr explicit expected(expected<U, G>&& rhs) {
- if (!rhs.has_value()) var_ = unexpected(std::move(rhs.error()));
- }
-
- template<class G = E _ENABLE_IF(
- std::is_constructible_v<E, const G&> &&
- std::is_convertible_v<const G&, E> /* non-explicit */
- )>
- // NOLINTNEXTLINE(google-explicit-constructor)
- constexpr expected(const unexpected<G>& e)
- : var_(std::in_place_index<1>, e.value()) {}
-
- template<class G = E _ENABLE_IF(
- std::is_constructible_v<E, const G&> &&
- !std::is_convertible_v<const G&, E> /* explicit */
- )>
- constexpr explicit expected(const unexpected<G>& e)
- : var_(std::in_place_index<1>, E(e.value())) {}
-
- template<class G = E _ENABLE_IF(
- std::is_constructible_v<E, G&&> &&
- std::is_convertible_v<G&&, E> /* non-explicit */
- )>
- // NOLINTNEXTLINE(google-explicit-constructor)
- constexpr expected(unexpected<G>&& e)
- : var_(std::in_place_index<1>, std::move(e.value())) {}
-
- template<class G = E _ENABLE_IF(
- std::is_constructible_v<E, G&&> &&
- !std::is_convertible_v<G&&, E> /* explicit */
- )>
- constexpr explicit expected(unexpected<G>&& e)
- : var_(std::in_place_index<1>, E(std::move(e.value()))) {}
-
- template<class... Args _ENABLE_IF(
- sizeof...(Args) == 0
- )>
- constexpr explicit expected(std::in_place_t, Args&&...) {}
-
- template<class... Args _ENABLE_IF(
- std::is_constructible_v<E, Args...>
- )>
- constexpr explicit expected(unexpect_t, Args&&... args)
- : var_(unexpected_type(std::forward<Args>(args)...)) {}
-
- template<class U, class... Args _ENABLE_IF(
- std::is_constructible_v<E, std::initializer_list<U>&, Args...>
- )>
- constexpr explicit expected(unexpect_t, std::initializer_list<U> il, Args&&... args)
- : var_(unexpected_type(il, std::forward<Args>(args)...)) {}
-
- // destructor
- ~expected() = default;
-
- // assignment
- // Note: SFNAIE doesn't work here because assignment operator should be
- // non-template. We could workaround this by defining a templated parent class
- // having the assignment operator. This incomplete implementation however
- // doesn't allow us to copy assign expected<T,E> even when T is non-copy
- // assignable. The copy assignment will fail by the underlying std::variant
- // anyway though the error message won't be clear.
- expected& operator=(const expected& rhs) = default;
-
- // Note for SFNAIE above applies to here as well
- expected& operator=(expected&& rhs) noexcept(std::is_nothrow_move_assignable_v<E>) = default;
-
- template<class G = E>
- expected& operator=(const unexpected<G>& rhs) {
- var_ = rhs;
- return *this;
- }
-
- template<class G = E _ENABLE_IF(
- std::is_nothrow_move_constructible_v<G> &&
- std::is_move_assignable_v<G>
- )>
- expected& operator=(unexpected<G>&& rhs) {
- var_ = std::move(rhs);
- return *this;
- }
-
- // modifiers
- void emplace() {
- var_ = std::monostate();
- }
-
- // swap
- template<typename = std::enable_if_t<
- std::is_swappable_v<E>>
- >
- void swap(expected& rhs) noexcept(std::is_nothrow_move_constructible_v<E>) {
- var_.swap(rhs.var_);
- }
-
- // observers
- constexpr explicit operator bool() const noexcept { return has_value(); }
- constexpr bool has_value() const noexcept { return var_.index() == 0; }
- constexpr bool ok() const noexcept { return has_value(); }
-
- constexpr void value() const& { if (!has_value()) std::get<0>(var_); }
-
- constexpr const E& error() const& { return std::get<unexpected_type>(var_).value(); }
- constexpr E& error() & { return std::get<unexpected_type>(var_).value(); }
- constexpr const E&& error() const&& { return std::move(std::get<unexpected_type>(var_)).value(); }
- constexpr E&& error() && { return std::move(std::get<unexpected_type>(var_)).value(); }
-
- // expected equality operators
- template<class E1, class E2>
- friend constexpr bool operator==(const expected<void, E1>& x, const expected<void, E2>& y);
-
- // Specialized algorithms
- template<class T1, class E1>
- friend void swap(expected<T1, E1>&, expected<T1, E1>&) noexcept;
-
- private:
- std::variant<std::monostate, unexpected_type> var_;
-};
-
-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;
- }
-}
-
-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;
- }
-}
-
-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;
- }
-}
-
-template<class E>
-class unexpected {
- public:
- // constructors
- constexpr unexpected(const unexpected&) = default;
- constexpr unexpected(unexpected&&) noexcept(std::is_nothrow_move_constructible_v<E>) = default;
-
- template <class Err = E _ENABLE_IF(
- 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)
- constexpr unexpected(Err&& e) : val_(std::forward<Err>(e)) {}
-
- template<class U, class... Args _ENABLE_IF(
- std::is_constructible_v<E, std::initializer_list<U>&, Args...>
- )>
- constexpr explicit unexpected(std::in_place_t, std::initializer_list<U> il, Args&&... args)
- : val_(il, std::forward<Args>(args)...) {}
-
- template<class Err _ENABLE_IF(
- std::is_constructible_v<E, Err> &&
- !std::is_constructible_v<E, unexpected<Err>&> &&
- !std::is_constructible_v<E, unexpected<Err>> &&
- !std::is_constructible_v<E, const unexpected<Err>&> &&
- !std::is_constructible_v<E, const unexpected<Err>> &&
- !std::is_convertible_v<unexpected<Err>&, E> &&
- !std::is_convertible_v<unexpected<Err>, E> &&
- !std::is_convertible_v<const unexpected<Err>&, E> &&
- !std::is_convertible_v<const unexpected<Err>, E> &&
- std::is_convertible_v<Err, E> /* non-explicit */
- )>
- // NOLINTNEXTLINE(google-explicit-constructor)
- constexpr unexpected(const unexpected<Err>& rhs)
- : val_(rhs.value()) {}
-
- template<class Err _ENABLE_IF(
- std::is_constructible_v<E, Err> &&
- !std::is_constructible_v<E, unexpected<Err>&> &&
- !std::is_constructible_v<E, unexpected<Err>> &&
- !std::is_constructible_v<E, const unexpected<Err>&> &&
- !std::is_constructible_v<E, const unexpected<Err>> &&
- !std::is_convertible_v<unexpected<Err>&, E> &&
- !std::is_convertible_v<unexpected<Err>, E> &&
- !std::is_convertible_v<const unexpected<Err>&, E> &&
- !std::is_convertible_v<const unexpected<Err>, E> &&
- !std::is_convertible_v<Err, E> /* explicit */
- )>
- constexpr explicit unexpected(const unexpected<Err>& rhs)
- : val_(E(rhs.value())) {}
-
- template<class Err _ENABLE_IF(
- std::is_constructible_v<E, Err> &&
- !std::is_constructible_v<E, unexpected<Err>&> &&
- !std::is_constructible_v<E, unexpected<Err>> &&
- !std::is_constructible_v<E, const unexpected<Err>&> &&
- !std::is_constructible_v<E, const unexpected<Err>> &&
- !std::is_convertible_v<unexpected<Err>&, E> &&
- !std::is_convertible_v<unexpected<Err>, E> &&
- !std::is_convertible_v<const unexpected<Err>&, E> &&
- !std::is_convertible_v<const unexpected<Err>, E> &&
- std::is_convertible_v<Err, E> /* non-explicit */
- )>
- // NOLINTNEXTLINE(google-explicit-constructor)
- constexpr unexpected(unexpected<Err>&& rhs)
- : val_(std::move(rhs.value())) {}
-
- template<class Err _ENABLE_IF(
- std::is_constructible_v<E, Err> &&
- !std::is_constructible_v<E, unexpected<Err>&> &&
- !std::is_constructible_v<E, unexpected<Err>> &&
- !std::is_constructible_v<E, const unexpected<Err>&> &&
- !std::is_constructible_v<E, const unexpected<Err>> &&
- !std::is_convertible_v<unexpected<Err>&, E> &&
- !std::is_convertible_v<unexpected<Err>, E> &&
- !std::is_convertible_v<const unexpected<Err>&, E> &&
- !std::is_convertible_v<const unexpected<Err>, E> &&
- !std::is_convertible_v<Err, E> /* explicit */
- )>
- constexpr explicit unexpected(unexpected<Err>&& rhs)
- : val_(E(std::move(rhs.value()))) {}
-
- // assignment
- constexpr unexpected& operator=(const unexpected&) = default;
- constexpr unexpected& operator=(unexpected&&) noexcept(std::is_nothrow_move_assignable_v<E>) =
- default;
- template<class Err = E>
- constexpr unexpected& operator=(const unexpected<Err>& rhs) {
- val_ = rhs.value();
- return *this;
- }
- template<class Err = E>
- constexpr unexpected& operator=(unexpected<Err>&& rhs) {
- val_ = std::forward<E>(rhs.value());
- return *this;
- }
-
- // observer
- constexpr const E& value() const& noexcept { return val_; }
- constexpr E& value() & noexcept { return val_; }
- constexpr const E&& value() const&& noexcept { return std::move(val_); }
- constexpr E&& value() && noexcept { return std::move(val_); }
-
- void swap(unexpected& other) noexcept(std::is_nothrow_swappable_v<E>) {
- std::swap(val_, other.val_);
- }
-
- template<class E1, class E2>
- friend constexpr bool
- operator==(const unexpected<E1>& e1, const unexpected<E2>& e2);
- template<class E1, class E2>
- friend constexpr bool
- operator!=(const unexpected<E1>& e1, const unexpected<E2>& e2);
-
- template<class E1>
- friend void swap(unexpected<E1>& x, unexpected<E1>& y) noexcept(noexcept(x.swap(y)));
-
- private:
- E val_;
-};
-
-template<class E1, class E2>
-constexpr bool
-operator==(const unexpected<E1>& e1, const unexpected<E2>& e2) {
- return e1.value() == e2.value();
-}
-
-template<class E1, class E2>
-constexpr bool
-operator!=(const unexpected<E1>& e1, const unexpected<E2>& e2) {
- return e1.value() != e2.value();
-}
-
-template<class E1>
-void swap(unexpected<E1>& x, unexpected<E1>& y) noexcept(noexcept(x.swap(y))) {
- x.swap(y);
-}
-
-// TODO: bad_expected_access class
-
-#undef _ENABLE_IF
-#undef _NODISCARD_
-
-} // namespace base
-} // namespace android
diff --git a/base/include/android-base/file.h b/base/include/android-base/file.h
deleted file mode 100644
index c622562..0000000
--- a/base/include/android-base/file.h
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include <string>
-
-#include "android-base/macros.h"
-#include "android-base/off64_t.h"
-#include "android-base/unique_fd.h"
-
-#if !defined(_WIN32) && !defined(O_BINARY)
-/** Windows needs O_BINARY, but Unix never mangles line endings. */
-#define O_BINARY 0
-#endif
-
-#if defined(_WIN32) && !defined(O_CLOEXEC)
-/** Windows has O_CLOEXEC but calls it O_NOINHERIT for some reason. */
-#define O_CLOEXEC O_NOINHERIT
-#endif
-
-class TemporaryFile {
- public:
- TemporaryFile();
- explicit TemporaryFile(const std::string& tmp_dir);
- ~TemporaryFile();
-
- // Release the ownership of fd, caller is reponsible for closing the
- // fd or stream properly.
- int release();
- // Don't remove the temporary file in the destructor.
- void DoNotRemove() { remove_file_ = false; }
-
- int fd;
- char path[1024];
-
- private:
- void init(const std::string& tmp_dir);
-
- bool remove_file_ = true;
-
- DISALLOW_COPY_AND_ASSIGN(TemporaryFile);
-};
-
-class TemporaryDir {
- public:
- TemporaryDir();
- ~TemporaryDir();
- // Don't remove the temporary dir in the destructor.
- void DoNotRemove() { remove_dir_and_contents_ = false; }
-
- char path[1024];
-
- private:
- bool init(const std::string& tmp_dir);
-
- bool remove_dir_and_contents_ = true;
-
- DISALLOW_COPY_AND_ASSIGN(TemporaryDir);
-};
-
-namespace android {
-namespace base {
-
-bool ReadFdToString(borrowed_fd fd, std::string* content);
-bool ReadFileToString(const std::string& path, std::string* content,
- bool follow_symlinks = false);
-
-bool WriteStringToFile(const std::string& content, const std::string& path,
- bool follow_symlinks = false);
-bool WriteStringToFd(const std::string& content, borrowed_fd fd);
-
-#if !defined(_WIN32)
-bool WriteStringToFile(const std::string& content, const std::string& path,
- mode_t mode, uid_t owner, gid_t group,
- bool follow_symlinks = false);
-#endif
-
-bool ReadFully(borrowed_fd fd, void* data, size_t byte_count);
-
-// Reads `byte_count` bytes from the file descriptor at the specified offset.
-// Returns false if there was an IO error or EOF was reached before reading `byte_count` bytes.
-//
-// NOTE: On Linux/Mac, this function wraps pread, which provides atomic read support without
-// modifying the read pointer of the file descriptor. On Windows, however, the read pointer does
-// get modified. This means that ReadFullyAtOffset can be used concurrently with other calls to the
-// same function, but concurrently seeking or reading incrementally can lead to unexpected
-// behavior.
-bool ReadFullyAtOffset(borrowed_fd fd, void* data, size_t byte_count, off64_t offset);
-
-bool WriteFully(borrowed_fd fd, const void* data, size_t byte_count);
-
-bool RemoveFileIfExists(const std::string& path, std::string* err = nullptr);
-
-#if !defined(_WIN32)
-bool Realpath(const std::string& path, std::string* result);
-bool Readlink(const std::string& path, std::string* result);
-#endif
-
-std::string GetExecutablePath();
-std::string GetExecutableDirectory();
-
-// Like the regular basename and dirname, but thread-safe on all
-// platforms and capable of correctly handling exotic Windows paths.
-std::string Basename(const std::string& path);
-std::string Dirname(const std::string& path);
-
-} // namespace base
-} // namespace android
diff --git a/base/include/android-base/format.h b/base/include/android-base/format.h
deleted file mode 100644
index 330040d..0000000
--- a/base/include/android-base/format.h
+++ /dev/null
@@ -1,28 +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
-
-// We include fmtlib here as an alias, since libbase will have fmtlib statically linked already.
-// It is accessed through its normal fmt:: namespace.
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wshadow"
-#include <fmt/chrono.h>
-#pragma clang diagnostic pop
-#include <fmt/core.h>
-#include <fmt/format.h>
-#include <fmt/ostream.h>
-#include <fmt/printf.h>
diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h
deleted file mode 100644
index accc225..0000000
--- a/base/include/android-base/logging.h
+++ /dev/null
@@ -1,462 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-//
-// Google-style C++ logging.
-//
-
-// This header provides a C++ stream interface to logging.
-//
-// To log:
-//
-// LOG(INFO) << "Some text; " << some_value;
-//
-// Replace `INFO` with any severity from `enum LogSeverity`.
-//
-// To log the result of a failed function and include the string
-// representation of `errno` at the end:
-//
-// PLOG(ERROR) << "Write failed";
-//
-// The output will be something like `Write failed: I/O error`.
-// Remember this as 'P' as in perror(3).
-//
-// To output your own types, simply implement operator<< as normal.
-//
-// By default, output goes to logcat on Android and stderr on the host.
-// A process can use `SetLogger` to decide where all logging goes.
-// Implementations are provided for logcat, stderr, and dmesg.
-//
-// By default, the process' name is used as the log tag.
-// Code can choose a specific log tag by defining LOG_TAG
-// before including this header.
-
-// This header also provides assertions:
-//
-// CHECK(must_be_true);
-// CHECK_EQ(a, b) << z_is_interesting_too;
-
-// NOTE: For Windows, you must include logging.h after windows.h to allow the
-// following code to suppress the evil ERROR macro:
-#ifdef _WIN32
-// windows.h includes wingdi.h which defines an evil macro ERROR.
-#ifdef ERROR
-#undef ERROR
-#endif
-#endif
-
-#include <functional>
-#include <memory>
-#include <ostream>
-
-#include "android-base/errno_restorer.h"
-#include "android-base/macros.h"
-
-// Note: DO NOT USE DIRECTLY. Use LOG_TAG instead.
-#ifdef _LOG_TAG_INTERNAL
-#error "_LOG_TAG_INTERNAL must not be defined"
-#endif
-#ifdef LOG_TAG
-#define _LOG_TAG_INTERNAL LOG_TAG
-#else
-#define _LOG_TAG_INTERNAL nullptr
-#endif
-
-namespace android {
-namespace base {
-
-enum LogSeverity {
- VERBOSE,
- DEBUG,
- INFO,
- WARNING,
- ERROR,
- FATAL_WITHOUT_ABORT, // For loggability tests, this is considered identical to FATAL.
- FATAL,
-};
-
-enum LogId {
- DEFAULT,
- MAIN,
- SYSTEM,
- RADIO,
- CRASH,
-};
-
-using LogFunction = std::function<void(LogId, LogSeverity, const char*, const char*,
- unsigned int, const char*)>;
-using AbortFunction = std::function<void(const char*)>;
-
-// Loggers for use with InitLogging/SetLogger.
-
-// Log to the kernel log (dmesg).
-void KernelLogger(LogId, LogSeverity, const char*, const char*, unsigned int, const char*);
-// Log to stderr in the full logcat format (with pid/tid/time/tag details).
-void StderrLogger(LogId, LogSeverity, const char*, const char*, unsigned int, const char*);
-// Log just the message to stdout/stderr (without pid/tid/time/tag details).
-// The choice of stdout versus stderr is based on the severity.
-// Errors are also prefixed by the program name (as with err(3)/error(3)).
-// Useful for replacing printf(3)/perror(3)/err(3)/error(3) in command-line tools.
-void StdioLogger(LogId, LogSeverity, const char*, const char*, unsigned int, const char*);
-
-void DefaultAborter(const char* abort_message);
-
-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.
-class LogdLogger {
- public:
- explicit LogdLogger(LogId default_log_id = android::base::MAIN);
-
- void operator()(LogId, LogSeverity, const char* tag, const char* file,
- unsigned int line, const char* message);
-
- private:
- LogId default_log_id_;
-};
-
-// Configure logging based on ANDROID_LOG_TAGS environment variable.
-// We need to parse a string that looks like
-//
-// *:v jdwp:d dalvikvm:d dalvikvm-gc:i dalvikvmi:i
-//
-// The tag (or '*' for the global level) comes first, followed by a colon and a
-// letter indicating the minimum priority level we're expected to log. This can
-// be used to reveal or conceal logs with specific tags.
-#ifdef __ANDROID__
-#define INIT_LOGGING_DEFAULT_LOGGER LogdLogger()
-#else
-#define INIT_LOGGING_DEFAULT_LOGGER StderrLogger
-#endif
-void InitLogging(char* argv[],
- LogFunction&& logger = INIT_LOGGING_DEFAULT_LOGGER,
- AbortFunction&& aborter = DefaultAborter);
-#undef INIT_LOGGING_DEFAULT_LOGGER
-
-// Replace the current logger.
-void SetLogger(LogFunction&& logger);
-
-// Replace the current aborter.
-void SetAborter(AbortFunction&& aborter);
-
-// A helper macro that produces an expression that accepts both a qualified name and an
-// unqualified name for a LogSeverity, and returns a LogSeverity value.
-// Note: DO NOT USE DIRECTLY. This is an implementation detail.
-#define SEVERITY_LAMBDA(severity) ([&]() { \
- using ::android::base::VERBOSE; \
- using ::android::base::DEBUG; \
- using ::android::base::INFO; \
- using ::android::base::WARNING; \
- using ::android::base::ERROR; \
- using ::android::base::FATAL_WITHOUT_ABORT; \
- using ::android::base::FATAL; \
- return (severity); }())
-
-#ifdef __clang_analyzer__
-// Clang's static analyzer does not see the conditional statement inside
-// LogMessage's destructor that will abort on FATAL severity.
-#define ABORT_AFTER_LOG_FATAL for (;; abort())
-
-struct LogAbortAfterFullExpr {
- ~LogAbortAfterFullExpr() __attribute__((noreturn)) { abort(); }
- explicit operator bool() const { return false; }
-};
-// Provides an expression that evaluates to the truthiness of `x`, automatically
-// aborting if `c` is true.
-#define ABORT_AFTER_LOG_EXPR_IF(c, x) (((c) && ::android::base::LogAbortAfterFullExpr()) || (x))
-// Note to the static analyzer that we always execute FATAL logs in practice.
-#define MUST_LOG_MESSAGE(severity) (SEVERITY_LAMBDA(severity) == ::android::base::FATAL)
-#else
-#define ABORT_AFTER_LOG_FATAL
-#define ABORT_AFTER_LOG_EXPR_IF(c, x) (x)
-#define MUST_LOG_MESSAGE(severity) false
-#endif
-#define ABORT_AFTER_LOG_FATAL_EXPR(x) ABORT_AFTER_LOG_EXPR_IF(true, x)
-
-// Defines whether the given severity will be logged or silently swallowed.
-#define WOULD_LOG(severity) \
- (UNLIKELY(::android::base::ShouldLog(SEVERITY_LAMBDA(severity), _LOG_TAG_INTERNAL)) || \
- MUST_LOG_MESSAGE(severity))
-
-// Get an ostream that can be used for logging at the given severity and to the default
-// destination.
-//
-// Notes:
-// 1) This will not check whether the severity is high enough. One should use WOULD_LOG to filter
-// usage manually.
-// 2) This does not save and restore errno.
-#define LOG_STREAM(severity) \
- ::android::base::LogMessage(__FILE__, __LINE__, SEVERITY_LAMBDA(severity), _LOG_TAG_INTERNAL, \
- -1) \
- .stream()
-
-// Logs a message to logcat on Android otherwise to stderr. If the severity is
-// FATAL it also causes an abort. For example:
-//
-// LOG(FATAL) << "We didn't expect to reach here";
-#define LOG(severity) LOGGING_PREAMBLE(severity) && LOG_STREAM(severity)
-
-// Checks if we want to log something, and sets up appropriate RAII objects if
-// so.
-// Note: DO NOT USE DIRECTLY. This is an implementation detail.
-#define LOGGING_PREAMBLE(severity) \
- (WOULD_LOG(severity) && \
- ABORT_AFTER_LOG_EXPR_IF((SEVERITY_LAMBDA(severity)) == ::android::base::FATAL, true) && \
- ::android::base::ErrnoRestorer())
-
-// A variant of LOG that also logs the current errno value. To be used when
-// library calls fail.
-#define PLOG(severity) \
- LOGGING_PREAMBLE(severity) && \
- ::android::base::LogMessage(__FILE__, __LINE__, SEVERITY_LAMBDA(severity), \
- _LOG_TAG_INTERNAL, errno) \
- .stream()
-
-// Marker that code is yet to be implemented.
-#define UNIMPLEMENTED(level) \
- LOG(level) << __PRETTY_FUNCTION__ << " unimplemented "
-
-// Check whether condition x holds and LOG(FATAL) if not. The value of the
-// expression x is only evaluated once. Extra logging can be appended using <<
-// after. For example:
-//
-// CHECK(false == true) results in a log message of
-// "Check failed: false == true".
-#define CHECK(x) \
- LIKELY((x)) || ABORT_AFTER_LOG_FATAL_EXPR(false) || \
- ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::FATAL, _LOG_TAG_INTERNAL, \
- -1) \
- .stream() \
- << "Check failed: " #x << " "
-
-// clang-format off
-// Helper for CHECK_xx(x,y) macros.
-#define CHECK_OP(LHS, RHS, OP) \
- for (auto _values = ::android::base::MakeEagerEvaluator(LHS, RHS); \
- UNLIKELY(!(_values.lhs OP _values.rhs)); \
- /* empty */) \
- ABORT_AFTER_LOG_FATAL \
- ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::FATAL, _LOG_TAG_INTERNAL, -1) \
- .stream() \
- << "Check failed: " << #LHS << " " << #OP << " " << #RHS << " (" #LHS "=" << _values.lhs \
- << ", " #RHS "=" << _values.rhs << ") "
-// clang-format on
-
-// Check whether a condition holds between x and y, LOG(FATAL) if not. The value
-// of the expressions x and y is evaluated once. Extra logging can be appended
-// using << after. For example:
-//
-// CHECK_NE(0 == 1, false) results in
-// "Check failed: false != false (0==1=false, false=false) ".
-#define CHECK_EQ(x, y) CHECK_OP(x, y, == )
-#define CHECK_NE(x, y) CHECK_OP(x, y, != )
-#define CHECK_LE(x, y) CHECK_OP(x, y, <= )
-#define CHECK_LT(x, y) CHECK_OP(x, y, < )
-#define CHECK_GE(x, y) CHECK_OP(x, y, >= )
-#define CHECK_GT(x, y) CHECK_OP(x, y, > )
-
-// clang-format off
-// Helper for CHECK_STRxx(s1,s2) macros.
-#define CHECK_STROP(s1, s2, sense) \
- while (UNLIKELY((strcmp(s1, s2) == 0) != (sense))) \
- ABORT_AFTER_LOG_FATAL \
- ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::FATAL, \
- _LOG_TAG_INTERNAL, -1) \
- .stream() \
- << "Check failed: " << "\"" << (s1) << "\"" \
- << ((sense) ? " == " : " != ") << "\"" << (s2) << "\""
-// clang-format on
-
-// Check for string (const char*) equality between s1 and s2, LOG(FATAL) if not.
-#define CHECK_STREQ(s1, s2) CHECK_STROP(s1, s2, true)
-#define CHECK_STRNE(s1, s2) CHECK_STROP(s1, s2, false)
-
-// Perform the pthread function call(args), LOG(FATAL) on error.
-#define CHECK_PTHREAD_CALL(call, args, what) \
- do { \
- int rc = call args; \
- if (rc != 0) { \
- errno = rc; \
- ABORT_AFTER_LOG_FATAL \
- PLOG(FATAL) << #call << " failed for " << (what); \
- } \
- } while (false)
-
-// CHECK that can be used in a constexpr function. For example:
-//
-// constexpr int half(int n) {
-// return
-// DCHECK_CONSTEXPR(n >= 0, , 0)
-// CHECK_CONSTEXPR((n & 1) == 0),
-// << "Extra debugging output: n = " << n, 0)
-// n / 2;
-// }
-#define CHECK_CONSTEXPR(x, out, dummy) \
- (UNLIKELY(!(x))) \
- ? (LOG(FATAL) << "Check failed: " << #x out, dummy) \
- :
-
-// DCHECKs are debug variants of CHECKs only enabled in debug builds. Generally
-// CHECK should be used unless profiling identifies a CHECK as being in
-// performance critical code.
-#if defined(NDEBUG) && !defined(__clang_analyzer__)
-static constexpr bool kEnableDChecks = false;
-#else
-static constexpr bool kEnableDChecks = true;
-#endif
-
-#define DCHECK(x) \
- if (::android::base::kEnableDChecks) CHECK(x)
-#define DCHECK_EQ(x, y) \
- if (::android::base::kEnableDChecks) CHECK_EQ(x, y)
-#define DCHECK_NE(x, y) \
- if (::android::base::kEnableDChecks) CHECK_NE(x, y)
-#define DCHECK_LE(x, y) \
- if (::android::base::kEnableDChecks) CHECK_LE(x, y)
-#define DCHECK_LT(x, y) \
- if (::android::base::kEnableDChecks) CHECK_LT(x, y)
-#define DCHECK_GE(x, y) \
- if (::android::base::kEnableDChecks) CHECK_GE(x, y)
-#define DCHECK_GT(x, y) \
- if (::android::base::kEnableDChecks) CHECK_GT(x, y)
-#define DCHECK_STREQ(s1, s2) \
- if (::android::base::kEnableDChecks) CHECK_STREQ(s1, s2)
-#define DCHECK_STRNE(s1, s2) \
- if (::android::base::kEnableDChecks) CHECK_STRNE(s1, s2)
-#if defined(NDEBUG) && !defined(__clang_analyzer__)
-#define DCHECK_CONSTEXPR(x, out, dummy)
-#else
-#define DCHECK_CONSTEXPR(x, out, dummy) CHECK_CONSTEXPR(x, out, dummy)
-#endif
-
-// Temporary class created to evaluate the LHS and RHS, used with
-// MakeEagerEvaluator to infer the types of LHS and RHS.
-template <typename LHS, typename RHS>
-struct EagerEvaluator {
- constexpr EagerEvaluator(LHS l, RHS r) : lhs(l), rhs(r) {
- }
- LHS lhs;
- RHS rhs;
-};
-
-// Helper function for CHECK_xx.
-template <typename LHS, typename RHS>
-constexpr EagerEvaluator<LHS, RHS> MakeEagerEvaluator(LHS lhs, RHS rhs) {
- return EagerEvaluator<LHS, RHS>(lhs, rhs);
-}
-
-// Explicitly instantiate EagerEvalue for pointers so that char*s aren't treated
-// as strings. To compare strings use CHECK_STREQ and CHECK_STRNE. We rely on
-// signed/unsigned warnings to protect you against combinations not explicitly
-// listed below.
-#define EAGER_PTR_EVALUATOR(T1, T2) \
- template <> \
- struct EagerEvaluator<T1, T2> { \
- EagerEvaluator(T1 l, T2 r) \
- : lhs(reinterpret_cast<const void*>(l)), \
- rhs(reinterpret_cast<const void*>(r)) { \
- } \
- const void* lhs; \
- const void* rhs; \
- }
-EAGER_PTR_EVALUATOR(const char*, const char*);
-EAGER_PTR_EVALUATOR(const char*, char*);
-EAGER_PTR_EVALUATOR(char*, const char*);
-EAGER_PTR_EVALUATOR(char*, char*);
-EAGER_PTR_EVALUATOR(const unsigned char*, const unsigned char*);
-EAGER_PTR_EVALUATOR(const unsigned char*, unsigned char*);
-EAGER_PTR_EVALUATOR(unsigned char*, const unsigned char*);
-EAGER_PTR_EVALUATOR(unsigned char*, unsigned char*);
-EAGER_PTR_EVALUATOR(const signed char*, const signed char*);
-EAGER_PTR_EVALUATOR(const signed char*, signed char*);
-EAGER_PTR_EVALUATOR(signed char*, const signed char*);
-EAGER_PTR_EVALUATOR(signed char*, signed char*);
-
-// Data for the log message, not stored in LogMessage to avoid increasing the
-// stack size.
-class LogMessageData;
-
-// A LogMessage is a temporarily scoped object used by LOG and the unlikely part
-// of a CHECK. The destructor will abort if the severity is FATAL.
-class LogMessage {
- public:
- // LogId has been deprecated, but this constructor must exist for prebuilts.
- LogMessage(const char* file, unsigned int line, LogId, LogSeverity severity, const char* tag,
- int error);
- LogMessage(const char* file, unsigned int line, LogSeverity severity, const char* tag, int error);
-
- ~LogMessage();
-
- // Returns the stream associated with the message, the LogMessage performs
- // output when it goes out of scope.
- std::ostream& stream();
-
- // The routine that performs the actual logging.
- static void LogLine(const char* file, unsigned int line, LogSeverity severity, const char* tag,
- const char* msg);
-
- private:
- const std::unique_ptr<LogMessageData> data_;
-
- DISALLOW_COPY_AND_ASSIGN(LogMessage);
-};
-
-// Get the minimum severity level for logging.
-LogSeverity GetMinimumLogSeverity();
-
-// Set the minimum severity level for logging, returning the old severity.
-LogSeverity SetMinimumLogSeverity(LogSeverity new_severity);
-
-// Return whether or not a log message with the associated tag should be logged.
-bool ShouldLog(LogSeverity severity, const char* tag);
-
-// Allows to temporarily change the minimum severity level for logging.
-class ScopedLogSeverity {
- public:
- explicit ScopedLogSeverity(LogSeverity level);
- ~ScopedLogSeverity();
-
- private:
- LogSeverity old_;
-};
-
-} // namespace base
-} // namespace android
-
-namespace std { // NOLINT(cert-dcl58-cpp)
-
-// Emit a warning of ostream<< with std::string*. The intention was most likely to print *string.
-//
-// Note: for this to work, we need to have this in a namespace.
-// Note: using a pragma because "-Wgcc-compat" (included in "-Weverything") complains about
-// diagnose_if.
-// Note: to print the pointer, use "<< static_cast<const void*>(string_pointer)" instead.
-// Note: a not-recommended alternative is to let Clang ignore the warning by adding
-// -Wno-user-defined-warnings to CPPFLAGS.
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wgcc-compat"
-#define OSTREAM_STRING_POINTER_USAGE_WARNING \
- __attribute__((diagnose_if(true, "Unexpected logging of string pointer", "warning")))
-inline OSTREAM_STRING_POINTER_USAGE_WARNING
-std::ostream& operator<<(std::ostream& stream, const std::string* string_pointer) {
- return stream << static_cast<const void*>(string_pointer);
-}
-#pragma clang diagnostic pop
-
-} // namespace std
diff --git a/base/include/android-base/macros.h b/base/include/android-base/macros.h
deleted file mode 100644
index 546b2ec..0000000
--- a/base/include/android-base/macros.h
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <stddef.h> // for size_t
-#include <unistd.h> // for TEMP_FAILURE_RETRY
-
-#include <utility>
-
-// bionic and glibc both have TEMP_FAILURE_RETRY, but eg Mac OS' libc doesn't.
-#ifndef TEMP_FAILURE_RETRY
-#define TEMP_FAILURE_RETRY(exp) \
- ({ \
- decltype(exp) _rc; \
- do { \
- _rc = (exp); \
- } while (_rc == -1 && errno == EINTR); \
- _rc; \
- })
-#endif
-
-// A macro to disallow the copy constructor and operator= functions
-// This must be placed in the private: declarations for a class.
-//
-// For disallowing only assign or copy, delete the relevant operator or
-// constructor, for example:
-// void operator=(const TypeName&) = delete;
-// Note, that most uses of DISALLOW_ASSIGN and DISALLOW_COPY are broken
-// semantically, one should either use disallow both or neither. Try to
-// avoid these in new code.
-#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
- TypeName(const TypeName&) = delete; \
- void operator=(const TypeName&) = delete
-
-// A macro to disallow all the implicit constructors, namely the
-// default constructor, copy constructor and operator= functions.
-//
-// This should be used in the private: declarations for a class
-// that wants to prevent anyone from instantiating it. This is
-// especially useful for classes containing only static methods.
-#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
- TypeName() = delete; \
- DISALLOW_COPY_AND_ASSIGN(TypeName)
-
-// The arraysize(arr) macro returns the # of elements in an array arr.
-// The expression is a compile-time constant, and therefore can be
-// used in defining new arrays, for example. If you use arraysize on
-// a pointer by mistake, you will get a compile-time error.
-//
-// One caveat is that arraysize() doesn't accept any array of an
-// anonymous type or a type defined inside a function. In these rare
-// cases, you have to use the unsafe ARRAYSIZE_UNSAFE() macro below. This is
-// due to a limitation in C++'s template system. The limitation might
-// eventually be removed, but it hasn't happened yet.
-
-// This template function declaration is used in defining arraysize.
-// Note that the function doesn't need an implementation, as we only
-// use its type.
-template <typename T, size_t N>
-char(&ArraySizeHelper(T(&array)[N]))[N]; // NOLINT(readability/casting)
-
-#define arraysize(array) (sizeof(ArraySizeHelper(array)))
-
-#define SIZEOF_MEMBER(t, f) sizeof(std::declval<t>().f)
-
-// Changing this definition will cause you a lot of pain. A majority of
-// vendor code defines LIKELY and UNLIKELY this way, and includes
-// this header through an indirect path.
-#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true ))
-#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false ))
-
-#define WARN_UNUSED __attribute__((warn_unused_result))
-
-// A deprecated function to call to create a false use of the parameter, for
-// example:
-// int foo(int x) { UNUSED(x); return 10; }
-// to avoid compiler warnings. Going forward we prefer ATTRIBUTE_UNUSED.
-template <typename... T>
-void UNUSED(const T&...) {
-}
-
-// An attribute to place on a parameter to a function, for example:
-// int foo(int x ATTRIBUTE_UNUSED) { return 10; }
-// to avoid compiler warnings.
-#define ATTRIBUTE_UNUSED __attribute__((__unused__))
-
-// The FALLTHROUGH_INTENDED macro can be used to annotate implicit fall-through
-// between switch labels:
-// switch (x) {
-// case 40:
-// case 41:
-// if (truth_is_out_there) {
-// ++x;
-// FALLTHROUGH_INTENDED; // Use instead of/along with annotations in
-// // comments.
-// } else {
-// return x;
-// }
-// case 42:
-// ...
-//
-// As shown in the example above, the FALLTHROUGH_INTENDED macro should be
-// followed by a semicolon. It is designed to mimic control-flow statements
-// like 'break;', so it can be placed in most places where 'break;' can, but
-// only if there are no statements on the execution path between it and the
-// next switch label.
-//
-// When compiled with clang, the FALLTHROUGH_INTENDED macro is expanded to
-// [[clang::fallthrough]] attribute, which is analysed when performing switch
-// labels fall-through diagnostic ('-Wimplicit-fallthrough'). See clang
-// documentation on language extensions for details:
-// http://clang.llvm.org/docs/LanguageExtensions.html#clang__fallthrough
-//
-// When used with unsupported compilers, the FALLTHROUGH_INTENDED macro has no
-// effect on diagnostics.
-//
-// In either case this macro has no effect on runtime behavior and performance
-// of code.
-#ifndef FALLTHROUGH_INTENDED
-#define FALLTHROUGH_INTENDED [[clang::fallthrough]] // NOLINT
-#endif
-
-// Current ABI string
-#if defined(__arm__)
-#define ABI_STRING "arm"
-#elif defined(__aarch64__)
-#define ABI_STRING "arm64"
-#elif defined(__i386__)
-#define ABI_STRING "x86"
-#elif defined(__x86_64__)
-#define ABI_STRING "x86_64"
-#endif
diff --git a/base/include/android-base/mapped_file.h b/base/include/android-base/mapped_file.h
deleted file mode 100644
index 8c37f43..0000000
--- a/base/include/android-base/mapped_file.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <sys/types.h>
-
-#include <memory>
-
-#include "android-base/macros.h"
-#include "android-base/off64_t.h"
-#include "android-base/unique_fd.h"
-
-#if defined(_WIN32)
-#include <windows.h>
-#define PROT_READ 1
-#define PROT_WRITE 2
-using os_handle = HANDLE;
-#else
-#include <sys/mman.h>
-using os_handle = int;
-#endif
-
-namespace android {
-namespace base {
-
-/**
- * A region of a file mapped into memory (for grepping: also known as MmapFile or file mapping).
- */
-class MappedFile {
- public:
- /**
- * Creates a new mapping of the file pointed to by `fd`. Unlike the underlying OS primitives,
- * `offset` does not need to be page-aligned. If `PROT_WRITE` is set in `prot`, the mapping
- * will be writable, otherwise it will be read-only. Mappings are always `MAP_SHARED`.
- */
- static std::unique_ptr<MappedFile> FromFd(borrowed_fd fd, off64_t offset, size_t length,
- int prot);
-
- /**
- * Same thing, but using the raw OS file handle instead of a CRT wrapper.
- */
- static std::unique_ptr<MappedFile> FromOsHandle(os_handle h, off64_t offset, size_t length,
- int prot);
-
- /**
- * Removes the mapping.
- */
- ~MappedFile();
-
- /**
- * Not copyable but movable.
- */
- MappedFile(MappedFile&& other);
- MappedFile& operator=(MappedFile&& other);
-
- char* data() const { return base_ + offset_; }
- size_t size() const { return size_; }
-
- private:
- DISALLOW_IMPLICIT_CONSTRUCTORS(MappedFile);
-
- void Close();
-
- char* base_;
- size_t size_;
-
- size_t offset_;
-
-#if defined(_WIN32)
- MappedFile(char* base, size_t size, size_t offset, HANDLE handle)
- : base_(base), size_(size), offset_(offset), handle_(handle) {}
- HANDLE handle_;
-#else
- MappedFile(char* base, size_t size, size_t offset) : base_(base), size_(size), offset_(offset) {}
-#endif
-};
-
-} // namespace base
-} // namespace android
diff --git a/base/include/android-base/memory.h b/base/include/android-base/memory.h
deleted file mode 100644
index 0277a03..0000000
--- a/base/include/android-base/memory.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-namespace android {
-namespace base {
-
-// Use memcpy for access to unaligned data on targets with alignment
-// restrictions. The compiler will generate appropriate code to access these
-// structures without generating alignment exceptions.
-template <typename T>
-static inline T get_unaligned(const void* address) {
- T result;
- memcpy(&result, address, sizeof(T));
- return result;
-}
-
-template <typename T>
-static inline void put_unaligned(void* address, T v) {
- memcpy(address, &v, sizeof(T));
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/include/android-base/no_destructor.h b/base/include/android-base/no_destructor.h
deleted file mode 100644
index ce0dc9f..0000000
--- a/base/include/android-base/no_destructor.h
+++ /dev/null
@@ -1,94 +0,0 @@
-#pragma once
-
-/*
- * 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 <utility>
-
-#include "android-base/macros.h"
-
-namespace android {
-namespace base {
-
-// A wrapper that makes it easy to create an object of type T with static
-// storage duration that:
-// - is only constructed on first access
-// - never invokes the destructor
-// in order to satisfy the styleguide ban on global constructors and
-// destructors.
-//
-// Runtime constant example:
-// const std::string& GetLineSeparator() {
-// // Forwards to std::string(size_t, char, const Allocator&) constructor.
-// static const base::NoDestructor<std::string> s(5, '-');
-// return *s;
-// }
-//
-// More complex initialization with a lambda:
-// const std::string& GetSessionNonce() {
-// static const base::NoDestructor<std::string> nonce([] {
-// std::string s(16);
-// crypto::RandString(s.data(), s.size());
-// return s;
-// }());
-// return *nonce;
-// }
-//
-// NoDestructor<T> stores the object inline, so it also avoids a pointer
-// indirection and a malloc. Also note that since C++11 static local variable
-// initialization is thread-safe and so is this pattern. Code should prefer to
-// use NoDestructor<T> over:
-// - A function scoped static T* or T& that is dynamically initialized.
-// - A global base::LazyInstance<T>.
-//
-// Note that since the destructor is never run, this *will* leak memory if used
-// as a stack or member variable. Furthermore, a NoDestructor<T> should never
-// have global scope as that may require a static initializer.
-template <typename T>
-class NoDestructor {
- public:
- // Not constexpr; just write static constexpr T x = ...; if the value should
- // be a constexpr.
- template <typename... Args>
- explicit NoDestructor(Args&&... args) {
- new (storage_) T(std::forward<Args>(args)...);
- }
-
- // Allows copy and move construction of the contained type, to allow
- // construction from an initializer list, e.g. for std::vector.
- explicit NoDestructor(const T& x) { new (storage_) T(x); }
- explicit NoDestructor(T&& x) { new (storage_) T(std::move(x)); }
-
- NoDestructor(const NoDestructor&) = delete;
- NoDestructor& operator=(const NoDestructor&) = delete;
-
- ~NoDestructor() = default;
-
- const T& operator*() const { return *get(); }
- T& operator*() { return *get(); }
-
- const T* operator->() const { return get(); }
- T* operator->() { return get(); }
-
- const T* get() const { return reinterpret_cast<const T*>(storage_); }
- T* get() { return reinterpret_cast<T*>(storage_); }
-
- private:
- alignas(T) char storage_[sizeof(T)];
-};
-
-} // namespace base
-} // namespace android
diff --git a/base/include/android-base/off64_t.h b/base/include/android-base/off64_t.h
deleted file mode 100644
index e6b71b8..0000000
--- a/base/include/android-base/off64_t.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#if defined(__APPLE__)
-/** Mac OS has always had a 64-bit off_t, so it doesn't have off64_t. */
-typedef off_t off64_t;
-#endif
diff --git a/base/include/android-base/parsebool.h b/base/include/android-base/parsebool.h
deleted file mode 100644
index b2bd021..0000000
--- a/base/include/android-base/parsebool.h
+++ /dev/null
@@ -1,58 +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 <string_view>
-
-namespace android {
-namespace base {
-
-// Parse the given string as yes or no inactivation of some sort. Return one of the
-// ParseBoolResult enumeration values.
-//
-// The following values parse as true:
-//
-// 1
-// on
-// true
-// y
-// yes
-//
-//
-// The following values parse as false:
-//
-// 0
-// false
-// n
-// no
-// off
-//
-// Anything else is a parse error.
-//
-// The purpose of this function is to have a single canonical parser for yes-or-no indications
-// throughout the system.
-
-enum class ParseBoolResult {
- kError,
- kFalse,
- kTrue,
-};
-
-ParseBoolResult ParseBool(std::string_view s);
-
-} // namespace base
-} // namespace android
diff --git a/base/include/android-base/parsedouble.h b/base/include/android-base/parsedouble.h
deleted file mode 100644
index ccffba2..0000000
--- a/base/include/android-base/parsedouble.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <errno.h>
-#include <stdlib.h>
-
-#include <limits>
-#include <string>
-
-namespace android {
-namespace base {
-
-// Parse floating value in the string 's' and sets 'out' to that value if it exists.
-// Optionally allows the caller to define a 'min' and 'max' beyond which
-// otherwise valid values will be rejected. Returns boolean success.
-template <typename T, T (*strtox)(const char* str, char** endptr)>
-static inline bool ParseFloatingPoint(const char* s, T* out, T min, T max) {
- errno = 0;
- char* end;
- T result = strtox(s, &end);
- if (errno != 0 || s == end || *end != '\0') {
- return false;
- }
- if (result < min || max < result) {
- return false;
- }
- if (out != nullptr) {
- *out = result;
- }
- return true;
-}
-
-// Parse double value in the string 's' and sets 'out' to that value if it exists.
-// Optionally allows the caller to define a 'min' and 'max' beyond which
-// otherwise valid values will be rejected. Returns boolean success.
-static inline bool ParseDouble(const char* s, double* out,
- double min = std::numeric_limits<double>::lowest(),
- double max = std::numeric_limits<double>::max()) {
- return ParseFloatingPoint<double, strtod>(s, out, min, max);
-}
-static inline bool ParseDouble(const std::string& s, double* out,
- double min = std::numeric_limits<double>::lowest(),
- double max = std::numeric_limits<double>::max()) {
- return ParseFloatingPoint<double, strtod>(s.c_str(), out, min, max);
-}
-
-// Parse float value in the string 's' and sets 'out' to that value if it exists.
-// Optionally allows the caller to define a 'min' and 'max' beyond which
-// otherwise valid values will be rejected. Returns boolean success.
-static inline bool ParseFloat(const char* s, float* out,
- float min = std::numeric_limits<float>::lowest(),
- float max = std::numeric_limits<float>::max()) {
- return ParseFloatingPoint<float, strtof>(s, out, min, max);
-}
-static inline bool ParseFloat(const std::string& s, float* out,
- float min = std::numeric_limits<float>::lowest(),
- float max = std::numeric_limits<float>::max()) {
- return ParseFloatingPoint<float, strtof>(s.c_str(), out, min, max);
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/include/android-base/parseint.h b/base/include/android-base/parseint.h
deleted file mode 100644
index be8b97b..0000000
--- a/base/include/android-base/parseint.h
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <limits>
-#include <string>
-#include <type_traits>
-
-namespace android {
-namespace base {
-
-// Parses the unsigned decimal or hexadecimal integer in the string 's' and sets
-// 'out' to that value if it is specified. Optionally allows the caller to define
-// a 'max' beyond which otherwise valid values will be rejected. Returns boolean
-// success; 'out' is untouched if parsing fails.
-template <typename T>
-bool ParseUint(const char* s, T* out, T max = std::numeric_limits<T>::max(),
- bool allow_suffixes = false) {
- static_assert(std::is_unsigned<T>::value, "ParseUint can only be used with unsigned types");
- while (isspace(*s)) {
- s++;
- }
-
- if (s[0] == '-') {
- errno = EINVAL;
- return false;
- }
-
- int base = (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) ? 16 : 10;
- errno = 0;
- char* end;
- unsigned long long int result = strtoull(s, &end, base);
- if (errno != 0) return false;
- if (end == s) {
- errno = EINVAL;
- return false;
- }
- if (*end != '\0') {
- const char* suffixes = "bkmgtpe";
- const char* suffix;
- if ((!allow_suffixes || (suffix = strchr(suffixes, tolower(*end))) == nullptr) ||
- __builtin_mul_overflow(result, 1ULL << (10 * (suffix - suffixes)), &result)) {
- errno = EINVAL;
- return false;
- }
- }
- if (max < result) {
- errno = ERANGE;
- return false;
- }
- if (out != nullptr) {
- *out = static_cast<T>(result);
- }
- return true;
-}
-
-// TODO: string_view
-template <typename T>
-bool ParseUint(const std::string& s, T* out, T max = std::numeric_limits<T>::max(),
- bool allow_suffixes = false) {
- return ParseUint(s.c_str(), out, max, allow_suffixes);
-}
-
-template <typename T>
-bool ParseByteCount(const char* s, T* out, T max = std::numeric_limits<T>::max()) {
- return ParseUint(s, out, max, true);
-}
-
-// TODO: string_view
-template <typename T>
-bool ParseByteCount(const std::string& s, T* out, T max = std::numeric_limits<T>::max()) {
- return ParseByteCount(s.c_str(), out, max);
-}
-
-// Parses the signed decimal or hexadecimal integer in the string 's' and sets
-// 'out' to that value if it is specified. Optionally allows the caller to define
-// a 'min' and 'max' beyond which otherwise valid values will be rejected. Returns
-// boolean success; 'out' is untouched if parsing fails.
-template <typename T>
-bool ParseInt(const char* s, T* out,
- T min = std::numeric_limits<T>::min(),
- T max = std::numeric_limits<T>::max()) {
- static_assert(std::is_signed<T>::value, "ParseInt can only be used with signed types");
- while (isspace(*s)) {
- s++;
- }
-
- int base = (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) ? 16 : 10;
- errno = 0;
- char* end;
- long long int result = strtoll(s, &end, base);
- if (errno != 0) {
- return false;
- }
- if (s == end || *end != '\0') {
- errno = EINVAL;
- return false;
- }
- if (result < min || max < result) {
- errno = ERANGE;
- return false;
- }
- if (out != nullptr) {
- *out = static_cast<T>(result);
- }
- return true;
-}
-
-// TODO: string_view
-template <typename T>
-bool ParseInt(const std::string& s, T* out,
- T min = std::numeric_limits<T>::min(),
- T max = std::numeric_limits<T>::max()) {
- return ParseInt(s.c_str(), out, min, max);
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/include/android-base/parsenetaddress.h b/base/include/android-base/parsenetaddress.h
deleted file mode 100644
index 47f8b5f..0000000
--- a/base/include/android-base/parsenetaddress.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <string>
-
-namespace android {
-namespace base {
-
-// Parses |address| into |host| and |port|.
-//
-// If |address| doesn't contain a port number, the default value is taken from
-// |port|. If |canonical_address| is non-null it will be set to "host:port" or
-// "[host]:port" as appropriate.
-//
-// On failure, returns false and fills |error|.
-bool ParseNetAddress(const std::string& address, std::string* host, int* port,
- std::string* canonical_address, std::string* error);
-
-} // namespace base
-} // namespace android
diff --git a/base/include/android-base/process.h b/base/include/android-base/process.h
deleted file mode 100644
index 69ed3fb..0000000
--- a/base/include/android-base/process.h
+++ /dev/null
@@ -1,60 +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 <dirent.h>
-#include <sys/types.h>
-
-#include <iterator>
-#include <memory>
-#include <vector>
-
-namespace android {
-namespace base {
-
-class AllPids {
- class PidIterator {
- public:
- PidIterator(DIR* dir) : dir_(dir, closedir) { Increment(); }
- PidIterator& operator++() {
- Increment();
- return *this;
- }
- bool operator==(const PidIterator& other) const { return pid_ == other.pid_; }
- bool operator!=(const PidIterator& other) const { return !(*this == other); }
- long operator*() const { return pid_; }
- // iterator traits
- using difference_type = pid_t;
- using value_type = pid_t;
- using pointer = const pid_t*;
- using reference = const pid_t&;
- using iterator_category = std::input_iterator_tag;
-
- private:
- void Increment();
-
- pid_t pid_ = -1;
- std::unique_ptr<DIR, decltype(&closedir)> dir_;
- };
-
- public:
- PidIterator begin() { return opendir("/proc"); }
- PidIterator end() { return nullptr; }
-};
-
-} // namespace base
-} // namespace android
diff --git a/base/include/android-base/properties.h b/base/include/android-base/properties.h
deleted file mode 100644
index 49f1f31..0000000
--- a/base/include/android-base/properties.h
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <sys/cdefs.h>
-
-#include <chrono>
-#include <limits>
-#include <optional>
-#include <string>
-
-struct prop_info;
-
-namespace android {
-namespace base {
-
-// Returns the current value of the system property `key`,
-// or `default_value` if the property is empty or doesn't exist.
-std::string GetProperty(const std::string& key, const std::string& default_value);
-
-// Returns true if the system property `key` has the value "1", "y", "yes", "on", or "true",
-// false for "0", "n", "no", "off", or "false", or `default_value` otherwise.
-bool GetBoolProperty(const std::string& key, bool default_value);
-
-// Returns the signed integer corresponding to the system property `key`.
-// If the property is empty, doesn't exist, doesn't have an integer value, or is outside
-// the optional bounds, returns `default_value`.
-template <typename T> T GetIntProperty(const std::string& key,
- T default_value,
- T min = std::numeric_limits<T>::min(),
- T max = std::numeric_limits<T>::max());
-
-// Returns the unsigned integer corresponding to the system property `key`.
-// If the property is empty, doesn't exist, doesn't have an integer value, or is outside
-// the optional bound, returns `default_value`.
-template <typename T> T GetUintProperty(const std::string& key,
- T default_value,
- T max = std::numeric_limits<T>::max());
-
-// Sets the system property `key` to `value`.
-bool SetProperty(const std::string& key, const std::string& value);
-
-// Waits for the system property `key` to have the value `expected_value`.
-// Times out after `relative_timeout`.
-// Returns true on success, false on timeout.
-#if defined(__BIONIC__)
-bool WaitForProperty(const std::string& key, const std::string& expected_value,
- std::chrono::milliseconds relative_timeout = std::chrono::milliseconds::max());
-#endif
-
-// Waits for the system property `key` to be created.
-// Times out after `relative_timeout`.
-// Returns true on success, false on timeout.
-#if defined(__BIONIC__)
-bool WaitForPropertyCreation(const std::string& key, std::chrono::milliseconds relative_timeout =
- std::chrono::milliseconds::max());
-#endif
-
-#if defined(__BIONIC__) && __cplusplus >= 201703L
-// Cached system property lookup. For code that needs to read the same property multiple times,
-// this class helps optimize those lookups.
-class CachedProperty {
- public:
- explicit CachedProperty(const char* property_name);
-
- // Returns the current value of the underlying system property as cheaply as possible.
- // The returned pointer is valid until the next call to Get. Because most callers are going
- // to want to parse the string returned here and cached that as well, this function performs
- // no locking, and is completely thread unsafe. It is the caller's responsibility to provide a
- // lock for thread-safety.
- //
- // Note: *changed can be set to true even if the contents of the property remain the same.
- const char* Get(bool* changed = nullptr);
-
- private:
- std::string property_name_;
- const prop_info* prop_info_;
- std::optional<uint32_t> cached_area_serial_;
- std::optional<uint32_t> cached_property_serial_;
- char cached_value_[92];
- bool is_read_only_;
- const char* read_only_property_;
-};
-#endif
-
-} // namespace base
-} // namespace android
diff --git a/base/include/android-base/result.h b/base/include/android-base/result.h
deleted file mode 100644
index 5e65876..0000000
--- a/base/include/android-base/result.h
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * 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.
- */
-
-// This file contains classes for returning a successful result along with an optional
-// arbitrarily typed return value or for returning a failure result along with an optional string
-// indicating why the function failed.
-
-// There are 3 classes that implement this functionality and one additional helper type.
-//
-// Result<T> either contains a member of type T that can be accessed using similar semantics as
-// std::optional<T> or it contains a ResultError describing an error, which can be accessed via
-// Result<T>::error().
-//
-// ResultError is a type that contains both a std::string describing the error and a copy of errno
-// from when the error occurred. ResultError can be used in an ostream directly to print its
-// string value.
-//
-// Result<void> is the correct return type for a function that either returns successfully or
-// returns an error value. Returning {} from a function that returns Result<void> is the
-// correct way to indicate that a function without a return type has completed successfully.
-//
-// A successful Result<T> is constructed implicitly from any type that can be implicitly converted
-// to T or from the constructor arguments for T. This allows you to return a type T directly from
-// a function that returns Result<T>.
-//
-// Error and ErrnoError are used to construct a Result<T> that has failed. The Error class takes
-// an ostream as an input and are implicitly cast to a Result<T> containing that failure.
-// ErrnoError() is a helper function to create an Error class that appends ": " + strerror(errno)
-// to the end of the failure string to aid in interacting with C APIs. Alternatively, an errno
-// value can be directly specified via the Error() constructor.
-//
-// Errorf and ErrnoErrorf accept the format string syntax of the fmblib (https://fmt.dev).
-// Errorf("{} errors", num) is equivalent to Error() << num << " errors".
-//
-// ResultError can be used in the ostream and when using Error/Errorf to construct a Result<T>.
-// In this case, the string that the ResultError takes is passed through the stream normally, but
-// the errno is passed to the Result<T>. This can be used to pass errno from a failing C function up
-// multiple callers. Note that when the outer Result<T> is created with ErrnoError/ErrnoErrorf then
-// the errno from the inner ResultError is not passed. Also when multiple ResultError objects are
-// used, the errno of the last one is respected.
-//
-// ResultError can also directly construct a Result<T>. This is particularly useful if you have a
-// function that return Result<T> but you have a Result<U> and want to return its error. In this
-// case, you can return the .error() from the Result<U> to construct the Result<T>.
-
-// An example of how to use these is below:
-// Result<U> CalculateResult(const T& input) {
-// U output;
-// if (!SomeOtherCppFunction(input, &output)) {
-// return Errorf("SomeOtherCppFunction {} failed", input);
-// }
-// if (!c_api_function(output)) {
-// return ErrnoErrorf("c_api_function {} failed", output);
-// }
-// return output;
-// }
-//
-// auto output = CalculateResult(input);
-// if (!output) return Error() << "CalculateResult failed: " << output.error();
-// UseOutput(*output);
-
-#pragma once
-
-#include <errno.h>
-
-#include <sstream>
-#include <string>
-
-#include "android-base/expected.h"
-#include "android-base/format.h"
-
-namespace android {
-namespace base {
-
-struct ResultError {
- template <typename T>
- ResultError(T&& message, int code) : message_(std::forward<T>(message)), code_(code) {}
-
- template <typename T>
- // NOLINTNEXTLINE(google-explicit-constructor)
- operator android::base::expected<T, ResultError>() {
- return android::base::unexpected(ResultError(message_, code_));
- }
-
- std::string message() const { return message_; }
- int code() const { return code_; }
-
- private:
- std::string message_;
- int code_;
-};
-
-inline bool operator==(const ResultError& lhs, const ResultError& rhs) {
- return lhs.message() == rhs.message() && lhs.code() == rhs.code();
-}
-
-inline bool operator!=(const ResultError& lhs, const ResultError& rhs) {
- return !(lhs == rhs);
-}
-
-inline std::ostream& operator<<(std::ostream& os, const ResultError& t) {
- os << t.message();
- return os;
-}
-
-class Error {
- public:
- Error() : errno_(0), append_errno_(false) {}
- // NOLINTNEXTLINE(google-explicit-constructor)
- Error(int errno_to_append) : errno_(errno_to_append), append_errno_(true) {}
-
- template <typename T>
- // NOLINTNEXTLINE(google-explicit-constructor)
- operator android::base::expected<T, ResultError>() {
- return android::base::unexpected(ResultError(str(), errno_));
- }
-
- template <typename T>
- Error& operator<<(T&& t) {
- if constexpr (std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, ResultError>) {
- errno_ = t.code();
- return (*this) << t.message();
- }
- int saved = errno;
- ss_ << t;
- errno = saved;
- return *this;
- }
-
- const std::string str() const {
- std::string str = ss_.str();
- if (append_errno_) {
- if (str.empty()) {
- return strerror(errno_);
- }
- return std::move(str) + ": " + strerror(errno_);
- }
- return str;
- }
-
- Error(const Error&) = delete;
- Error(Error&&) = delete;
- Error& operator=(const Error&) = delete;
- Error& operator=(Error&&) = delete;
-
- template <typename T, typename... Args>
- friend Error ErrorfImpl(const T&& fmt, const Args&... args);
-
- template <typename T, typename... Args>
- friend Error ErrnoErrorfImpl(const T&& fmt, const Args&... args);
-
- private:
- Error(bool append_errno, int errno_to_append, const std::string& message)
- : errno_(errno_to_append), append_errno_(append_errno) {
- (*this) << message;
- }
-
- std::stringstream ss_;
- int errno_;
- const bool append_errno_;
-};
-
-inline Error ErrnoError() {
- return Error(errno);
-}
-
-inline int ErrorCode(int code) {
- return code;
-}
-
-// Return the error code of the last ResultError object, if any.
-// Otherwise, return `code` as it is.
-template <typename T, typename... Args>
-inline int ErrorCode(int code, T&& t, const Args&... args) {
- if constexpr (std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, ResultError>) {
- return ErrorCode(t.code(), args...);
- }
- return ErrorCode(code, args...);
-}
-
-template <typename T, typename... Args>
-inline Error ErrorfImpl(const T&& fmt, const Args&... args) {
- return Error(false, ErrorCode(0, args...), fmt::format(fmt, args...));
-}
-
-template <typename T, typename... Args>
-inline Error ErrnoErrorfImpl(const T&& fmt, const Args&... args) {
- return Error(true, errno, fmt::format(fmt, args...));
-}
-
-#define Errorf(fmt, ...) android::base::ErrorfImpl(FMT_STRING(fmt), ##__VA_ARGS__)
-#define ErrnoErrorf(fmt, ...) android::base::ErrnoErrorfImpl(FMT_STRING(fmt), ##__VA_ARGS__)
-
-template <typename T>
-using Result = android::base::expected<T, ResultError>;
-
-// Macros for testing the results of functions that return android::base::Result.
-// These also work with base::android::expected.
-
-#define CHECK_RESULT_OK(stmt) \
- do { \
- const auto& tmp = (stmt); \
- CHECK(tmp.ok()) << tmp.error(); \
- } while (0)
-
-#define ASSERT_RESULT_OK(stmt) \
- do { \
- const auto& tmp = (stmt); \
- ASSERT_TRUE(tmp.ok()) << tmp.error(); \
- } while (0)
-
-#define EXPECT_RESULT_OK(stmt) \
- do { \
- auto tmp = (stmt); \
- EXPECT_TRUE(tmp.ok()) << tmp.error(); \
- } while (0)
-
-// TODO: Maybe add RETURN_IF_ERROR() and ASSIGN_OR_RETURN()
-
-} // namespace base
-} // namespace android
diff --git a/base/include/android-base/scopeguard.h b/base/include/android-base/scopeguard.h
deleted file mode 100644
index 5a224d6..0000000
--- a/base/include/android-base/scopeguard.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <utility> // for std::move, std::forward
-
-namespace android {
-namespace base {
-
-// ScopeGuard ensures that the specified functor is executed no matter how the
-// current scope exits.
-template <typename F>
-class ScopeGuard {
- public:
- ScopeGuard(F&& f) : f_(std::forward<F>(f)), active_(true) {}
-
- ScopeGuard(ScopeGuard&& that) noexcept : f_(std::move(that.f_)), active_(that.active_) {
- that.active_ = false;
- }
-
- template <typename Functor>
- ScopeGuard(ScopeGuard<Functor>&& that) : f_(std::move(that.f_)), active_(that.active_) {
- that.active_ = false;
- }
-
- ~ScopeGuard() {
- if (active_) f_();
- }
-
- ScopeGuard() = delete;
- ScopeGuard(const ScopeGuard&) = delete;
- void operator=(const ScopeGuard&) = delete;
- void operator=(ScopeGuard&& that) = delete;
-
- void Disable() { active_ = false; }
-
- bool active() const { return active_; }
-
- private:
- template <typename Functor>
- friend class ScopeGuard;
-
- F f_;
- bool active_;
-};
-
-template <typename F>
-ScopeGuard<F> make_scope_guard(F&& f) {
- return ScopeGuard<F>(std::forward<F>(f));
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/include/android-base/stringprintf.h b/base/include/android-base/stringprintf.h
deleted file mode 100644
index 93c56af..0000000
--- a/base/include/android-base/stringprintf.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2011 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 <stdarg.h>
-#include <string>
-
-namespace android {
-namespace base {
-
-// These printf-like functions are implemented in terms of vsnprintf, so they
-// use the same attribute for compile-time format string checking.
-
-// Returns a string corresponding to printf-like formatting of the arguments.
-std::string StringPrintf(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)));
-
-// Appends a printf-like formatting of the arguments to 'dst'.
-void StringAppendF(std::string* dst, const char* fmt, ...)
- __attribute__((__format__(__printf__, 2, 3)));
-
-// Appends a printf-like formatting of the arguments to 'dst'.
-void StringAppendV(std::string* dst, const char* format, va_list ap)
- __attribute__((__format__(__printf__, 2, 0)));
-
-} // namespace base
-} // namespace android
diff --git a/base/include/android-base/strings.h b/base/include/android-base/strings.h
deleted file mode 100644
index 14d534a..0000000
--- a/base/include/android-base/strings.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <sstream>
-#include <string>
-#include <string_view>
-#include <vector>
-
-namespace android {
-namespace base {
-
-// Splits a string into a vector of strings.
-//
-// The string is split at each occurrence of a character in delimiters.
-//
-// The empty string is not a valid delimiter list.
-std::vector<std::string> Split(const std::string& s,
- const std::string& delimiters);
-
-// Trims whitespace off both ends of the given string.
-std::string Trim(const std::string& s);
-
-// Joins a container of things into a single string, using the given separator.
-template <typename ContainerT, typename SeparatorT>
-std::string Join(const ContainerT& things, SeparatorT separator) {
- if (things.empty()) {
- return "";
- }
-
- std::ostringstream result;
- result << *things.begin();
- for (auto it = std::next(things.begin()); it != things.end(); ++it) {
- result << separator << *it;
- }
- return result.str();
-}
-
-// We instantiate the common cases in strings.cpp.
-extern template std::string Join(const std::vector<std::string>&, char);
-extern template std::string Join(const std::vector<const char*>&, char);
-extern template std::string Join(const std::vector<std::string>&, const std::string&);
-extern template std::string Join(const std::vector<const char*>&, const std::string&);
-
-// Tests whether 's' starts with 'prefix'.
-bool StartsWith(std::string_view s, std::string_view prefix);
-bool StartsWith(std::string_view s, char prefix);
-bool StartsWithIgnoreCase(std::string_view s, std::string_view prefix);
-
-// Tests whether 's' ends with 'suffix'.
-bool EndsWith(std::string_view s, std::string_view suffix);
-bool EndsWith(std::string_view s, char suffix);
-bool EndsWithIgnoreCase(std::string_view s, std::string_view suffix);
-
-// Tests whether 'lhs' equals 'rhs', ignoring case.
-bool EqualsIgnoreCase(std::string_view lhs, std::string_view rhs);
-
-// Removes `prefix` from the start of the given string and returns true (if
-// it was present), false otherwise.
-inline bool ConsumePrefix(std::string_view* s, std::string_view prefix) {
- if (!StartsWith(*s, prefix)) return false;
- s->remove_prefix(prefix.size());
- return true;
-}
-
-// Removes `suffix` from the end of the given string and returns true (if
-// it was present), false otherwise.
-inline bool ConsumeSuffix(std::string_view* s, std::string_view suffix) {
- if (!EndsWith(*s, suffix)) return false;
- s->remove_suffix(suffix.size());
- return true;
-}
-
-// Replaces `from` with `to` in `s`, once if `all == false`, or as many times as
-// there are matches if `all == true`.
-[[nodiscard]] std::string StringReplace(std::string_view s, std::string_view from,
- std::string_view to, bool all);
-
-} // namespace base
-} // namespace android
diff --git a/base/include/android-base/test_utils.h b/base/include/android-base/test_utils.h
deleted file mode 100644
index f3d7cb0..0000000
--- a/base/include/android-base/test_utils.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <regex>
-#include <string>
-
-#include <android-base/file.h>
-#include <android-base/macros.h>
-
-class CapturedStdFd {
- public:
- CapturedStdFd(int std_fd);
- ~CapturedStdFd();
-
- std::string str();
-
- void Start();
- void Stop();
- void Reset();
-
- private:
- int fd() const;
-
- TemporaryFile temp_file_;
- int std_fd_;
- int old_fd_ = -1;
-
- DISALLOW_COPY_AND_ASSIGN(CapturedStdFd);
-};
-
-class CapturedStderr : public CapturedStdFd {
- public:
- CapturedStderr() : CapturedStdFd(STDERR_FILENO) {}
-};
-
-class CapturedStdout : public CapturedStdFd {
- public:
- CapturedStdout() : CapturedStdFd(STDOUT_FILENO) {}
-};
-
-#define ASSERT_MATCH(str, pattern) \
- do { \
- auto __s = (str); \
- if (!std::regex_search(__s, std::regex((pattern)))) { \
- FAIL() << "regex mismatch: expected " << (pattern) << " in:\n" << __s; \
- } \
- } while (0)
-
-#define ASSERT_NOT_MATCH(str, pattern) \
- do { \
- auto __s = (str); \
- if (std::regex_search(__s, std::regex((pattern)))) { \
- FAIL() << "regex mismatch: expected to not find " << (pattern) << " in:\n" << __s; \
- } \
- } while (0)
-
-#define EXPECT_MATCH(str, pattern) \
- do { \
- auto __s = (str); \
- if (!std::regex_search(__s, std::regex((pattern)))) { \
- ADD_FAILURE() << "regex mismatch: expected " << (pattern) << " in:\n" << __s; \
- } \
- } while (0)
-
-#define EXPECT_NOT_MATCH(str, pattern) \
- do { \
- auto __s = (str); \
- if (std::regex_search(__s, std::regex((pattern)))) { \
- ADD_FAILURE() << "regex mismatch: expected to not find " << (pattern) << " in:\n" << __s; \
- } \
- } while (0)
diff --git a/base/include/android-base/thread_annotations.h b/base/include/android-base/thread_annotations.h
deleted file mode 100644
index 53fe6da..0000000
--- a/base/include/android-base/thread_annotations.h
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <mutex>
-
-#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
-
-#define CAPABILITY(x) \
- THREAD_ANNOTATION_ATTRIBUTE__(capability(x))
-
-#define SCOPED_CAPABILITY \
- THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
-
-#define SHARED_CAPABILITY(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(shared_capability(__VA_ARGS__))
-
-#define GUARDED_BY(x) \
- THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
-
-#define PT_GUARDED_BY(x) \
- THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))
-
-#define EXCLUSIVE_LOCKS_REQUIRED(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__))
-
-#define SHARED_LOCKS_REQUIRED(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__))
-
-#define ACQUIRED_BEFORE(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))
-
-#define ACQUIRED_AFTER(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__))
-
-#define REQUIRES(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__))
-
-#define REQUIRES_SHARED(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(__VA_ARGS__))
-
-#define ACQUIRE(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__))
-
-#define ACQUIRE_SHARED(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__))
-
-#define RELEASE(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__))
-
-#define RELEASE_SHARED(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__))
-
-#define TRY_ACQUIRE(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__))
-
-#define TRY_ACQUIRE_SHARED(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__))
-
-#define EXCLUDES(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))
-
-#define ASSERT_CAPABILITY(x) \
- THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x))
-
-#define ASSERT_SHARED_CAPABILITY(x) \
- THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x))
-
-#define RETURN_CAPABILITY(x) \
- THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
-
-#define EXCLUSIVE_LOCK_FUNCTION(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__))
-
-#define EXCLUSIVE_TRYLOCK_FUNCTION(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__))
-
-#define SHARED_LOCK_FUNCTION(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__))
-
-#define SHARED_TRYLOCK_FUNCTION(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__))
-
-#define UNLOCK_FUNCTION(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__))
-
-#define SCOPED_LOCKABLE \
- THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
-
-#define LOCK_RETURNED(x) \
- THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
-
-#define NO_THREAD_SAFETY_ANALYSIS \
- THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
-
-namespace android {
-namespace base {
-
-// A class to help thread safety analysis deal with std::unique_lock and condition_variable.
-//
-// Clang's thread safety analysis currently doesn't perform alias analysis, so movable types
-// like std::unique_lock can't be marked with thread safety annotations. This helper allows
-// for manual assertion of lock state in a scope.
-//
-// For example:
-//
-// std::mutex mutex;
-// std::condition_variable cv;
-// std::vector<int> vec GUARDED_BY(mutex);
-//
-// int pop() {
-// std::unique_lock lock(mutex);
-// ScopedLockAssertion lock_assertion(mutex);
-// cv.wait(lock, []() {
-// ScopedLockAssertion lock_assertion(mutex);
-// return !vec.empty();
-// });
-//
-// int result = vec.back();
-// vec.pop_back();
-// return result;
-// }
-class SCOPED_CAPABILITY ScopedLockAssertion {
- public:
- ScopedLockAssertion(std::mutex& mutex) ACQUIRE(mutex) {}
- ~ScopedLockAssertion() RELEASE() {}
-};
-
-} // namespace base
-} // namespace android
diff --git a/base/include/android-base/threads.h b/base/include/android-base/threads.h
deleted file mode 100644
index dba1fc6..0000000
--- a/base/include/android-base/threads.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <stdint.h>
-
-namespace android {
-namespace base {
-uint64_t GetThreadId();
-}
-} // namespace android
-
-#if defined(__GLIBC__)
-// bionic has this Linux-specifix call, but glibc doesn't.
-extern "C" int tgkill(int tgid, int tid, int sig);
-#endif
diff --git a/base/include/android-base/unique_fd.h b/base/include/android-base/unique_fd.h
deleted file mode 100644
index c4a0aad..0000000
--- a/base/include/android-base/unique_fd.h
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-
-#if !defined(_WIN32)
-#include <sys/socket.h>
-#endif
-
-#include <stdio.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-// DO NOT INCLUDE OTHER LIBBASE HEADERS!
-// This file gets used in libbinder, and libbinder is used everywhere.
-// Including other headers from libbase frequently results in inclusion of
-// android-base/macros.h, which causes macro collisions.
-
-// Container for a file descriptor that automatically closes the descriptor as
-// it goes out of scope.
-//
-// unique_fd ufd(open("/some/path", "r"));
-// if (ufd.get() == -1) return error;
-//
-// // Do something useful, possibly including 'return'.
-//
-// return 0; // Descriptor is closed for you.
-//
-// unique_fd is also known as ScopedFd/ScopedFD/scoped_fd; mentioned here to help
-// you find this class if you're searching for one of those names.
-
-#if defined(__BIONIC__)
-#include <android/fdsan.h>
-#endif
-
-namespace android {
-namespace base {
-
-struct DefaultCloser {
-#if defined(__BIONIC__)
- static void Tag(int fd, void* old_addr, void* new_addr) {
- if (android_fdsan_exchange_owner_tag) {
- uint64_t old_tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_UNIQUE_FD,
- reinterpret_cast<uint64_t>(old_addr));
- uint64_t new_tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_UNIQUE_FD,
- reinterpret_cast<uint64_t>(new_addr));
- android_fdsan_exchange_owner_tag(fd, old_tag, new_tag);
- }
- }
- static void Close(int fd, void* addr) {
- if (android_fdsan_close_with_tag) {
- uint64_t tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_UNIQUE_FD,
- reinterpret_cast<uint64_t>(addr));
- android_fdsan_close_with_tag(fd, tag);
- } else {
- close(fd);
- }
- }
-#else
- static void Close(int fd) {
- // Even if close(2) fails with EINTR, the fd will have been closed.
- // Using TEMP_FAILURE_RETRY will either lead to EBADF or closing someone
- // else's fd.
- // http://lkml.indiana.edu/hypermail/linux/kernel/0509.1/0877.html
- ::close(fd);
- }
-#endif
-};
-
-template <typename Closer>
-class unique_fd_impl final {
- public:
- unique_fd_impl() {}
-
- explicit unique_fd_impl(int fd) { reset(fd); }
- ~unique_fd_impl() { reset(); }
-
- unique_fd_impl(const unique_fd_impl&) = delete;
- void operator=(const unique_fd_impl&) = delete;
- unique_fd_impl(unique_fd_impl&& other) noexcept { reset(other.release()); }
- unique_fd_impl& operator=(unique_fd_impl&& s) noexcept {
- int fd = s.fd_;
- s.fd_ = -1;
- reset(fd, &s);
- return *this;
- }
-
- void reset(int new_value = -1) { reset(new_value, nullptr); }
-
- int get() const { return fd_; }
-
-#if !defined(ANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION)
- // unique_fd's operator int is dangerous, but we have way too much code that
- // depends on it, so make this opt-in at first.
- operator int() const { return get(); } // NOLINT
-#endif
-
- bool operator>=(int rhs) const { return get() >= rhs; }
- bool operator<(int rhs) const { return get() < rhs; }
- bool operator==(int rhs) const { return get() == rhs; }
- bool operator!=(int rhs) const { return get() != rhs; }
- bool operator==(const unique_fd_impl& rhs) const { return get() == rhs.get(); }
- bool operator!=(const unique_fd_impl& rhs) const { return get() != rhs.get(); }
-
- // Catch bogus error checks (i.e.: "!fd" instead of "fd != -1").
- bool operator!() const = delete;
-
- bool ok() const { return get() >= 0; }
-
- int release() __attribute__((warn_unused_result)) {
- tag(fd_, this, nullptr);
- int ret = fd_;
- fd_ = -1;
- return ret;
- }
-
- private:
- void reset(int new_value, void* previous_tag) {
- int previous_errno = errno;
-
- if (fd_ != -1) {
- close(fd_, this);
- }
-
- fd_ = new_value;
- if (new_value != -1) {
- tag(new_value, previous_tag, this);
- }
-
- errno = previous_errno;
- }
-
- int fd_ = -1;
-
- // Template magic to use Closer::Tag if available, and do nothing if not.
- // If Closer::Tag exists, this implementation is preferred, because int is a better match.
- // If not, this implementation is SFINAEd away, and the no-op below is the only one that exists.
- template <typename T = Closer>
- static auto tag(int fd, void* old_tag, void* new_tag)
- -> decltype(T::Tag(fd, old_tag, new_tag), void()) {
- T::Tag(fd, old_tag, new_tag);
- }
-
- template <typename T = Closer>
- static void tag(long, void*, void*) {
- // No-op.
- }
-
- // Same as above, to select between Closer::Close(int) and Closer::Close(int, void*).
- template <typename T = Closer>
- static auto close(int fd, void* tag_value) -> decltype(T::Close(fd, tag_value), void()) {
- T::Close(fd, tag_value);
- }
-
- template <typename T = Closer>
- static auto close(int fd, void*) -> decltype(T::Close(fd), void()) {
- T::Close(fd);
- }
-};
-
-using unique_fd = unique_fd_impl<DefaultCloser>;
-
-#if !defined(_WIN32)
-
-// Inline functions, so that they can be used header-only.
-template <typename Closer>
-inline bool Pipe(unique_fd_impl<Closer>* read, unique_fd_impl<Closer>* write,
- int flags = O_CLOEXEC) {
- int pipefd[2];
-
-#if defined(__linux__)
- if (pipe2(pipefd, flags) != 0) {
- return false;
- }
-#else // defined(__APPLE__)
- if (flags & ~(O_CLOEXEC | O_NONBLOCK)) {
- return false;
- }
- if (pipe(pipefd) != 0) {
- return false;
- }
-
- if (flags & O_CLOEXEC) {
- if (fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) != 0 || fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) != 0) {
- close(pipefd[0]);
- close(pipefd[1]);
- return false;
- }
- }
- if (flags & O_NONBLOCK) {
- if (fcntl(pipefd[0], F_SETFL, O_NONBLOCK) != 0 || fcntl(pipefd[1], F_SETFL, O_NONBLOCK) != 0) {
- close(pipefd[0]);
- close(pipefd[1]);
- return false;
- }
- }
-#endif
-
- read->reset(pipefd[0]);
- write->reset(pipefd[1]);
- return true;
-}
-
-template <typename Closer>
-inline bool Socketpair(int domain, int type, int protocol, unique_fd_impl<Closer>* left,
- unique_fd_impl<Closer>* right) {
- int sockfd[2];
- if (socketpair(domain, type, protocol, sockfd) != 0) {
- return false;
- }
- left->reset(sockfd[0]);
- right->reset(sockfd[1]);
- return true;
-}
-
-template <typename Closer>
-inline bool Socketpair(int type, unique_fd_impl<Closer>* left, unique_fd_impl<Closer>* right) {
- return Socketpair(AF_UNIX, type, 0, left, right);
-}
-
-// Using fdopen with unique_fd correctly is more annoying than it should be,
-// because fdopen doesn't close the file descriptor received upon failure.
-inline FILE* Fdopen(unique_fd&& ufd, const char* mode) {
- int fd = ufd.release();
- FILE* file = fdopen(fd, mode);
- if (!file) {
- close(fd);
- }
- return file;
-}
-
-// Using fdopendir with unique_fd correctly is more annoying than it should be,
-// because fdopen doesn't close the file descriptor received upon failure.
-inline DIR* Fdopendir(unique_fd&& ufd) {
- int fd = ufd.release();
- DIR* dir = fdopendir(fd);
- if (dir == nullptr) {
- close(fd);
- }
- return dir;
-}
-
-#endif // !defined(_WIN32)
-
-// A wrapper type that can be implicitly constructed from either int or unique_fd.
-struct borrowed_fd {
- /* implicit */ borrowed_fd(int fd) : fd_(fd) {} // NOLINT
- template <typename T>
- /* implicit */ borrowed_fd(const unique_fd_impl<T>& ufd) : fd_(ufd.get()) {} // NOLINT
-
- int get() const { return fd_; }
-
- bool operator>=(int rhs) const { return get() >= rhs; }
- bool operator<(int rhs) const { return get() < rhs; }
- bool operator==(int rhs) const { return get() == rhs; }
- bool operator!=(int rhs) const { return get() != rhs; }
-
- private:
- int fd_ = -1;
-};
-} // namespace base
-} // namespace android
-
-template <typename T>
-int close(const android::base::unique_fd_impl<T>&)
- __attribute__((__unavailable__("close called on unique_fd")));
-
-template <typename T>
-FILE* fdopen(const android::base::unique_fd_impl<T>&, const char* mode)
- __attribute__((__unavailable__("fdopen takes ownership of the fd passed in; either dup the "
- "unique_fd, or use android::base::Fdopen to pass ownership")));
-
-template <typename T>
-DIR* fdopendir(const android::base::unique_fd_impl<T>&) __attribute__((
- __unavailable__("fdopendir takes ownership of the fd passed in; either dup the "
- "unique_fd, or use android::base::Fdopendir to pass ownership")));
diff --git a/base/include/android-base/utf8.h b/base/include/android-base/utf8.h
deleted file mode 100644
index 1a414ec..0000000
--- a/base/include/android-base/utf8.h
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#ifdef _WIN32
-#include <sys/types.h>
-#include <string>
-#else
-// Bring in prototypes for standard APIs so that we can import them into the utf8 namespace.
-#include <fcntl.h> // open
-#include <stdio.h> // fopen
-#include <sys/stat.h> // mkdir
-#include <unistd.h> // unlink
-#endif
-
-namespace android {
-namespace base {
-
-// Only available on Windows because this is only needed on Windows.
-#ifdef _WIN32
-// Convert size number of UTF-16 wchar_t's to UTF-8. Returns whether the
-// conversion was done successfully.
-bool WideToUTF8(const wchar_t* utf16, const size_t size, std::string* utf8);
-
-// Convert a NULL-terminated string of UTF-16 characters to UTF-8. Returns
-// whether the conversion was done successfully.
-bool WideToUTF8(const wchar_t* utf16, std::string* utf8);
-
-// Convert a UTF-16 std::wstring (including any embedded NULL characters) to
-// UTF-8. Returns whether the conversion was done successfully.
-bool WideToUTF8(const std::wstring& utf16, std::string* utf8);
-
-// Convert size number of UTF-8 char's to UTF-16. Returns whether the conversion
-// was done successfully.
-bool UTF8ToWide(const char* utf8, const size_t size, std::wstring* utf16);
-
-// Convert a NULL-terminated string of UTF-8 characters to UTF-16. Returns
-// whether the conversion was done successfully.
-bool UTF8ToWide(const char* utf8, std::wstring* utf16);
-
-// Convert a UTF-8 std::string (including any embedded NULL characters) to
-// UTF-16. Returns whether the conversion was done successfully.
-bool UTF8ToWide(const std::string& utf8, std::wstring* utf16);
-
-// Convert a file system path, represented as a NULL-terminated string of
-// UTF-8 characters, to a UTF-16 string representing the same file system
-// path using the Windows extended-lengh path representation.
-//
-// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#MAXPATH:
-// ```The Windows API has many functions that also have Unicode versions to
-// permit an extended-length path for a maximum total path length of 32,767
-// characters. To specify an extended-length path, use the "\\?\" prefix.
-// For example, "\\?\D:\very long path".```
-//
-// Returns whether the conversion was done successfully.
-bool UTF8PathToWindowsLongPath(const char* utf8, std::wstring* utf16);
-#endif
-
-// The functions in the utf8 namespace take UTF-8 strings. For Windows, these
-// are wrappers, for non-Windows these just expose existing APIs. To call these
-// functions, use:
-//
-// // anonymous namespace to avoid conflict with existing open(), unlink(), etc.
-// namespace {
-// // Import functions into anonymous namespace.
-// using namespace android::base::utf8;
-//
-// void SomeFunction(const char* name) {
-// int fd = open(name, ...); // Calls android::base::utf8::open().
-// ...
-// unlink(name); // Calls android::base::utf8::unlink().
-// }
-// }
-namespace utf8 {
-
-#ifdef _WIN32
-FILE* fopen(const char* name, const char* mode);
-int mkdir(const char* name, mode_t mode);
-int open(const char* name, int flags, ...);
-int unlink(const char* name);
-#else
-using ::fopen;
-using ::mkdir;
-using ::open;
-using ::unlink;
-#endif
-
-} // namespace utf8
-} // namespace base
-} // namespace android
diff --git a/base/liblog_symbols.cpp b/base/liblog_symbols.cpp
deleted file mode 100644
index d5dfcd2..0000000
--- a/base/liblog_symbols.cpp
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "liblog_symbols.h"
-
-#if defined(__ANDROID__) && !defined(NO_LIBLOG_DLSYM)
-#include <dlfcn.h>
-#endif
-
-namespace android {
-namespace base {
-
-#if defined(__ANDROID__) && !defined(NO_LIBLOG_DLSYM)
-
-const std::optional<LibLogFunctions>& GetLibLogFunctions() {
- static std::optional<LibLogFunctions> liblog_functions = []() -> std::optional<LibLogFunctions> {
- void* liblog_handle = dlopen("liblog.so", RTLD_NOW);
- if (liblog_handle == nullptr) {
- return {};
- }
-
- LibLogFunctions real_liblog_functions = {};
-
-#define DLSYM(name) \
- real_liblog_functions.name = \
- reinterpret_cast<decltype(LibLogFunctions::name)>(dlsym(liblog_handle, #name)); \
- if (real_liblog_functions.name == nullptr) { \
- return {}; \
- }
-
- DLSYM(__android_log_set_logger)
- DLSYM(__android_log_write_logger_data)
- DLSYM(__android_log_logd_logger)
- DLSYM(__android_log_stderr_logger)
- DLSYM(__android_log_set_aborter)
- DLSYM(__android_log_call_aborter)
- DLSYM(__android_log_default_aborter)
- DLSYM(__android_log_set_minimum_priority);
- DLSYM(__android_log_get_minimum_priority);
- DLSYM(__android_log_set_default_tag);
-#undef DLSYM
-
- return real_liblog_functions;
- }();
-
- return liblog_functions;
-}
-
-#else
-
-const std::optional<LibLogFunctions>& GetLibLogFunctions() {
- static std::optional<LibLogFunctions> liblog_functions = []() -> std::optional<LibLogFunctions> {
- return LibLogFunctions{
- .__android_log_set_logger = __android_log_set_logger,
- .__android_log_write_logger_data = __android_log_write_logger_data,
- .__android_log_logd_logger = __android_log_logd_logger,
- .__android_log_stderr_logger = __android_log_stderr_logger,
- .__android_log_set_aborter = __android_log_set_aborter,
- .__android_log_call_aborter = __android_log_call_aborter,
- .__android_log_default_aborter = __android_log_default_aborter,
- .__android_log_set_minimum_priority = __android_log_set_minimum_priority,
- .__android_log_get_minimum_priority = __android_log_get_minimum_priority,
- .__android_log_set_default_tag = __android_log_set_default_tag,
- };
- }();
- return liblog_functions;
-}
-
-#endif
-
-} // namespace base
-} // namespace android
diff --git a/base/liblog_symbols.h b/base/liblog_symbols.h
deleted file mode 100644
index d3134e9..0000000
--- a/base/liblog_symbols.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <optional>
-
-#include <android/log.h>
-
-namespace android {
-namespace base {
-
-struct LibLogFunctions {
- void (*__android_log_set_logger)(__android_logger_function logger);
- void (*__android_log_write_logger_data)(struct __android_logger_data* logger_data,
- const char* msg);
-
- void (*__android_log_logd_logger)(const struct __android_logger_data* logger_data,
- const char* msg);
- void (*__android_log_stderr_logger)(const struct __android_logger_data* logger_data,
- const char* message);
-
- void (*__android_log_set_aborter)(__android_aborter_function aborter);
- void (*__android_log_call_aborter)(const char* abort_message);
- void (*__android_log_default_aborter)(const char* abort_message);
- int (*__android_log_set_minimum_priority)(int priority);
- int (*__android_log_get_minimum_priority)();
- void (*__android_log_set_default_tag)(const char* tag);
-};
-
-const std::optional<LibLogFunctions>& GetLibLogFunctions();
-
-} // namespace base
-} // namespace android
diff --git a/base/logging.cpp b/base/logging.cpp
deleted file mode 100644
index f42b996..0000000
--- a/base/logging.cpp
+++ /dev/null
@@ -1,639 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#if defined(_WIN32)
-#include <windows.h>
-#endif
-
-#include "android-base/logging.h"
-
-#include <fcntl.h>
-#include <inttypes.h>
-#include <libgen.h>
-#include <time.h>
-
-// For getprogname(3) or program_invocation_short_name.
-#if defined(__ANDROID__) || defined(__APPLE__)
-#include <stdlib.h>
-#elif defined(__GLIBC__)
-#include <errno.h>
-#endif
-
-#if defined(__linux__)
-#include <sys/uio.h>
-#endif
-
-#include <atomic>
-#include <iostream>
-#include <limits>
-#include <mutex>
-#include <optional>
-#include <sstream>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include <android/log.h>
-#ifdef __ANDROID__
-#include <android/set_abort_message.h>
-#else
-#include <sys/types.h>
-#include <unistd.h>
-#endif
-
-#include <android-base/file.h>
-#include <android-base/macros.h>
-#include <android-base/parseint.h>
-#include <android-base/strings.h>
-#include <android-base/threads.h>
-
-#include "liblog_symbols.h"
-
-namespace android {
-namespace base {
-
-// BSD-based systems like Android/macOS have getprogname(). Others need us to provide one.
-#if defined(__GLIBC__) || defined(_WIN32)
-static const char* getprogname() {
-#if defined(__GLIBC__)
- return program_invocation_short_name;
-#elif defined(_WIN32)
- static bool first = true;
- static char progname[MAX_PATH] = {};
-
- if (first) {
- snprintf(progname, sizeof(progname), "%s",
- android::base::Basename(android::base::GetExecutablePath()).c_str());
- first = false;
- }
-
- return progname;
-#endif
-}
-#endif
-
-static const char* GetFileBasename(const char* file) {
- // We can't use basename(3) even on Unix because the Mac doesn't
- // have a non-modifying basename.
- const char* last_slash = strrchr(file, '/');
- if (last_slash != nullptr) {
- return last_slash + 1;
- }
-#if defined(_WIN32)
- const char* last_backslash = strrchr(file, '\\');
- if (last_backslash != nullptr) {
- return last_backslash + 1;
- }
-#endif
- return file;
-}
-
-#if defined(__linux__)
-static int OpenKmsg() {
-#if defined(__ANDROID__)
- // pick up 'file w /dev/kmsg' environment from daemon's init rc file
- const auto val = getenv("ANDROID_FILE__dev_kmsg");
- if (val != nullptr) {
- int fd;
- if (android::base::ParseInt(val, &fd, 0)) {
- auto flags = fcntl(fd, F_GETFL);
- if ((flags != -1) && ((flags & O_ACCMODE) == O_WRONLY)) return fd;
- }
- }
-#endif
- return TEMP_FAILURE_RETRY(open("/dev/kmsg", O_WRONLY | O_CLOEXEC));
-}
-#endif
-
-static LogId log_id_tToLogId(int buffer_id) {
- switch (buffer_id) {
- case LOG_ID_MAIN:
- return MAIN;
- case LOG_ID_SYSTEM:
- return SYSTEM;
- case LOG_ID_RADIO:
- return RADIO;
- case LOG_ID_CRASH:
- return CRASH;
- case LOG_ID_DEFAULT:
- default:
- return DEFAULT;
- }
-}
-
-static int LogIdTolog_id_t(LogId log_id) {
- switch (log_id) {
- case MAIN:
- return LOG_ID_MAIN;
- case SYSTEM:
- return LOG_ID_SYSTEM;
- case RADIO:
- return LOG_ID_RADIO;
- case CRASH:
- return LOG_ID_CRASH;
- case DEFAULT:
- default:
- return LOG_ID_DEFAULT;
- }
-}
-
-static LogSeverity PriorityToLogSeverity(int priority) {
- switch (priority) {
- case ANDROID_LOG_DEFAULT:
- return INFO;
- case ANDROID_LOG_VERBOSE:
- return VERBOSE;
- case ANDROID_LOG_DEBUG:
- return DEBUG;
- case ANDROID_LOG_INFO:
- return INFO;
- case ANDROID_LOG_WARN:
- return WARNING;
- case ANDROID_LOG_ERROR:
- return ERROR;
- case ANDROID_LOG_FATAL:
- return FATAL;
- default:
- return FATAL;
- }
-}
-
-static android_LogPriority LogSeverityToPriority(LogSeverity severity) {
- switch (severity) {
- case VERBOSE:
- return ANDROID_LOG_VERBOSE;
- case DEBUG:
- return ANDROID_LOG_DEBUG;
- case INFO:
- return ANDROID_LOG_INFO;
- case WARNING:
- return ANDROID_LOG_WARN;
- case ERROR:
- return ANDROID_LOG_ERROR;
- case FATAL_WITHOUT_ABORT:
- case FATAL:
- default:
- return ANDROID_LOG_FATAL;
- }
-}
-
-static std::mutex& LoggingLock() {
- static auto& logging_lock = *new std::mutex();
- return logging_lock;
-}
-
-// Only used for Q fallback.
-static LogFunction& Logger() {
-#ifdef __ANDROID__
- static auto& logger = *new LogFunction(LogdLogger());
-#else
- static auto& logger = *new LogFunction(StderrLogger);
-#endif
- return logger;
-}
-
-// Only used for Q fallback.
-static AbortFunction& Aborter() {
- static auto& aborter = *new AbortFunction(DefaultAborter);
- return aborter;
-}
-
-// Only used for Q fallback.
-static std::recursive_mutex& TagLock() {
- static auto& tag_lock = *new std::recursive_mutex();
- return tag_lock;
-}
-// Only used for Q fallback.
-static std::string* gDefaultTag;
-
-void SetDefaultTag(const std::string& tag) {
- static auto& liblog_functions = GetLibLogFunctions();
- if (liblog_functions) {
- liblog_functions->__android_log_set_default_tag(tag.c_str());
- } else {
- std::lock_guard<std::recursive_mutex> lock(TagLock());
- if (gDefaultTag != nullptr) {
- delete gDefaultTag;
- gDefaultTag = nullptr;
- }
- if (!tag.empty()) {
- gDefaultTag = new std::string(tag);
- }
- }
-}
-
-static bool gInitialized = false;
-
-// Only used for Q fallback.
-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) {
- // clang-format off
- static constexpr int kLogSeverityToKernelLogLevel[] = {
- [android::base::VERBOSE] = 7, // KERN_DEBUG (there is no verbose kernel log
- // level)
- [android::base::DEBUG] = 7, // KERN_DEBUG
- [android::base::INFO] = 6, // KERN_INFO
- [android::base::WARNING] = 4, // KERN_WARNING
- [android::base::ERROR] = 3, // KERN_ERROR
- [android::base::FATAL_WITHOUT_ABORT] = 2, // KERN_CRIT
- [android::base::FATAL] = 2, // KERN_CRIT
- };
- // clang-format on
- static_assert(arraysize(kLogSeverityToKernelLogLevel) == android::base::FATAL + 1,
- "Mismatch in size of kLogSeverityToKernelLogLevel and values in LogSeverity");
-
- static int klog_fd = OpenKmsg();
- if (klog_fd == -1) return;
-
- int level = kLogSeverityToKernelLogLevel[severity];
-
- // The kernel's printk buffer is only 1024 bytes.
- // 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);
- if (size > sizeof(buf)) {
- size = snprintf(buf, sizeof(buf), "<%d>%s: %zu-byte message too long for printk\n",
- level, tag, size);
- }
-
- iovec iov[1];
- iov[0].iov_base = buf;
- iov[0].iov_len = size;
- TEMP_FAILURE_RETRY(writev(klog_fd, iov, 1));
-}
-#endif
-
-void StderrLogger(LogId, LogSeverity severity, const char* tag, const char* file, unsigned int line,
- const char* message) {
- struct tm now;
- time_t t = time(nullptr);
-
-#if defined(_WIN32)
- localtime_s(&now, &t);
-#else
- localtime_r(&t, &now);
-#endif
-
- 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);
- }
-}
-
-void StdioLogger(LogId, LogSeverity severity, const char* /*tag*/, const char* /*file*/,
- unsigned int /*line*/, const char* message) {
- if (severity >= WARNING) {
- fflush(stdout);
- fprintf(stderr, "%s: %s\n", GetFileBasename(getprogname()), message);
- } else {
- fprintf(stdout, "%s\n", message);
- }
-}
-
-void DefaultAborter(const char* abort_message) {
-#ifdef __ANDROID__
- android_set_abort_message(abort_message);
-#else
- UNUSED(abort_message);
-#endif
- 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) {
- android_LogPriority priority = LogSeverityToPriority(severity);
- if (id == DEFAULT) {
- id = default_log_id_;
- }
-
- int 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;
- }
-
- static auto& liblog_functions = GetLibLogFunctions();
- if (liblog_functions) {
- __android_logger_data logger_data = {sizeof(__android_logger_data), lg_id, priority, tag,
- static_cast<const char*>(nullptr), 0};
- liblog_functions->__android_log_logd_logger(&logger_data, message);
- } else {
- __android_log_buf_print(lg_id, priority, tag, "%s", message);
- }
-}
-
-void InitLogging(char* argv[], LogFunction&& logger, AbortFunction&& aborter) {
- SetLogger(std::forward<LogFunction>(logger));
- SetAborter(std::forward<AbortFunction>(aborter));
-
- if (gInitialized) {
- return;
- }
-
- gInitialized = true;
-
- // Stash the command line for later use. We can use /proc/self/cmdline on
- // Linux to recover this, but we don't have that luxury on the Mac/Windows,
- // and there are a couple of argv[0] variants that are commonly used.
- if (argv != nullptr) {
- SetDefaultTag(basename(argv[0]));
- }
-
- const char* tags = getenv("ANDROID_LOG_TAGS");
- if (tags == nullptr) {
- return;
- }
-
- std::vector<std::string> specs = Split(tags, " ");
- for (size_t i = 0; i < specs.size(); ++i) {
- // "tag-pattern:[vdiwefs]"
- std::string spec(specs[i]);
- if (spec.size() == 3 && StartsWith(spec, "*:")) {
- switch (spec[2]) {
- case 'v':
- SetMinimumLogSeverity(VERBOSE);
- continue;
- case 'd':
- SetMinimumLogSeverity(DEBUG);
- continue;
- case 'i':
- SetMinimumLogSeverity(INFO);
- continue;
- case 'w':
- SetMinimumLogSeverity(WARNING);
- continue;
- case 'e':
- SetMinimumLogSeverity(ERROR);
- continue;
- case 'f':
- SetMinimumLogSeverity(FATAL_WITHOUT_ABORT);
- continue;
- // liblog will even suppress FATAL if you say 's' for silent, but that's
- // crazy!
- case 's':
- SetMinimumLogSeverity(FATAL_WITHOUT_ABORT);
- continue;
- }
- }
- LOG(FATAL) << "unsupported '" << spec << "' in ANDROID_LOG_TAGS (" << tags
- << ")";
- }
-}
-
-void SetLogger(LogFunction&& logger) {
- static auto& liblog_functions = GetLibLogFunctions();
- if (liblog_functions) {
- // We need to atomically swap the old and new pointers since other threads may be logging.
- // We know all threads will be using the new logger after __android_log_set_logger() returns,
- // so we can delete it then.
- // This leaks one std::function<> per instance of libbase if multiple copies of libbase within a
- // single process call SetLogger(). That is the same cost as having a static
- // std::function<>, which is the not-thread-safe alternative.
- static std::atomic<LogFunction*> logger_function(nullptr);
- auto* old_logger_function = logger_function.exchange(new LogFunction(logger));
- liblog_functions->__android_log_set_logger([](const struct __android_logger_data* logger_data,
- const char* message) {
- auto log_id = log_id_tToLogId(logger_data->buffer_id);
- auto severity = PriorityToLogSeverity(logger_data->priority);
-
- auto& function = *logger_function.load(std::memory_order_acquire);
- function(log_id, severity, logger_data->tag, logger_data->file, logger_data->line, message);
- });
- delete old_logger_function;
- } else {
- std::lock_guard<std::mutex> lock(LoggingLock());
- Logger() = std::move(logger);
- }
-}
-
-void SetAborter(AbortFunction&& aborter) {
- static auto& liblog_functions = GetLibLogFunctions();
- if (liblog_functions) {
- // See the comment in SetLogger().
- static std::atomic<AbortFunction*> abort_function(nullptr);
- auto* old_abort_function = abort_function.exchange(new AbortFunction(aborter));
- __android_log_set_aborter([](const char* abort_message) {
- auto& function = *abort_function.load(std::memory_order_acquire);
- function(abort_message);
- });
- delete old_abort_function;
- } else {
- std::lock_guard<std::mutex> lock(LoggingLock());
- Aborter() = std::move(aborter);
- }
-}
-
-// This indirection greatly reduces the stack impact of having lots of
-// checks/logging in a function.
-class LogMessageData {
- public:
- LogMessageData(const char* file, unsigned int line, LogSeverity severity, const char* tag,
- int error)
- : file_(GetFileBasename(file)),
- line_number_(line),
- severity_(severity),
- tag_(tag),
- error_(error) {}
-
- const char* GetFile() const {
- return file_;
- }
-
- unsigned int GetLineNumber() const {
- return line_number_;
- }
-
- LogSeverity GetSeverity() const {
- return severity_;
- }
-
- const char* GetTag() const { return tag_; }
-
- int GetError() const {
- return error_;
- }
-
- std::ostream& GetBuffer() {
- return buffer_;
- }
-
- std::string ToString() const {
- return buffer_.str();
- }
-
- private:
- std::ostringstream buffer_;
- const char* const file_;
- const unsigned int line_number_;
- const LogSeverity severity_;
- const char* const tag_;
- const int error_;
-
- DISALLOW_COPY_AND_ASSIGN(LogMessageData);
-};
-
-LogMessage::LogMessage(const char* file, unsigned int line, LogId, LogSeverity severity,
- const char* tag, int error)
- : LogMessage(file, line, severity, tag, error) {}
-
-LogMessage::LogMessage(const char* file, unsigned int line, LogSeverity severity, const char* tag,
- int error)
- : data_(new LogMessageData(file, line, severity, tag, error)) {}
-
-LogMessage::~LogMessage() {
- // Check severity again. This is duplicate work wrt/ LOG macros, but not LOG_STREAM.
- if (!WOULD_LOG(data_->GetSeverity())) {
- return;
- }
-
- // Finish constructing the message.
- if (data_->GetError() != -1) {
- data_->GetBuffer() << ": " << strerror(data_->GetError());
- }
- std::string msg(data_->ToString());
-
- if (data_->GetSeverity() == FATAL) {
-#ifdef __ANDROID__
- // Set the bionic abort message early to avoid liblog doing it
- // with the individual lines, so that we get the whole message.
- android_set_abort_message(msg.c_str());
-#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;
- }
- }
- }
-
- // Abort if necessary.
- if (data_->GetSeverity() == FATAL) {
- static auto& liblog_functions = GetLibLogFunctions();
- if (liblog_functions) {
- liblog_functions->__android_log_call_aborter(msg.c_str());
- } else {
- Aborter()(msg.c_str());
- }
- }
-}
-
-std::ostream& LogMessage::stream() {
- return data_->GetBuffer();
-}
-
-void LogMessage::LogLine(const char* file, unsigned int line, LogSeverity severity, const char* tag,
- const char* message) {
- static auto& liblog_functions = GetLibLogFunctions();
- auto priority = LogSeverityToPriority(severity);
- if (liblog_functions) {
- __android_logger_data logger_data = {
- sizeof(__android_logger_data), LOG_ID_DEFAULT, priority, tag, file, line};
- __android_log_write_logger_data(&logger_data, message);
- } else {
- if (tag == nullptr) {
- std::lock_guard<std::recursive_mutex> lock(TagLock());
- if (gDefaultTag == nullptr) {
- gDefaultTag = new std::string(getprogname());
- }
-
- Logger()(DEFAULT, severity, gDefaultTag->c_str(), file, line, message);
- } else {
- Logger()(DEFAULT, severity, tag, file, line, message);
- }
- }
-}
-
-LogSeverity GetMinimumLogSeverity() {
- static auto& liblog_functions = GetLibLogFunctions();
- if (liblog_functions) {
- return PriorityToLogSeverity(liblog_functions->__android_log_get_minimum_priority());
- } else {
- return gMinimumLogSeverity;
- }
-}
-
-bool ShouldLog(LogSeverity severity, const char* tag) {
- static auto& liblog_functions = GetLibLogFunctions();
- // Even though we're not using the R liblog functions in this function, if we're running on Q,
- // we need to fall back to using gMinimumLogSeverity, since __android_log_is_loggable() will not
- // take into consideration the value from SetMinimumLogSeverity().
- if (liblog_functions) {
- int priority = LogSeverityToPriority(severity);
- return __android_log_is_loggable(priority, tag, ANDROID_LOG_INFO);
- } else {
- return severity >= gMinimumLogSeverity;
- }
-}
-
-LogSeverity SetMinimumLogSeverity(LogSeverity new_severity) {
- static auto& liblog_functions = GetLibLogFunctions();
- if (liblog_functions) {
- auto priority = LogSeverityToPriority(new_severity);
- return PriorityToLogSeverity(liblog_functions->__android_log_set_minimum_priority(priority));
- } else {
- LogSeverity old_severity = gMinimumLogSeverity;
- gMinimumLogSeverity = new_severity;
- return old_severity;
- }
-}
-
-ScopedLogSeverity::ScopedLogSeverity(LogSeverity new_severity) {
- old_ = SetMinimumLogSeverity(new_severity);
-}
-
-ScopedLogSeverity::~ScopedLogSeverity() {
- SetMinimumLogSeverity(old_);
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/logging_test.cpp b/base/logging_test.cpp
deleted file mode 100644
index 3a453e6..0000000
--- a/base/logging_test.cpp
+++ /dev/null
@@ -1,619 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/logging.h"
-
-#include <libgen.h>
-
-#if defined(_WIN32)
-#include <signal.h>
-#endif
-
-#include <regex>
-#include <string>
-
-#include "android-base/file.h"
-#include "android-base/stringprintf.h"
-#include "android-base/test_utils.h"
-
-#include <gtest/gtest.h>
-
-#ifdef __ANDROID__
-#define HOST_TEST(suite, name) TEST(suite, DISABLED_ ## name)
-#else
-#define HOST_TEST(suite, name) TEST(suite, name)
-#endif
-
-#if defined(_WIN32)
-static void ExitSignalAbortHandler(int) {
- _exit(3);
-}
-#endif
-
-static void SuppressAbortUI() {
-#if defined(_WIN32)
- // We really just want to call _set_abort_behavior(0, _CALL_REPORTFAULT) to
- // suppress the Windows Error Reporting dialog box, but that API is not
- // available in the OS-supplied C Runtime, msvcrt.dll, that we currently
- // use (it is available in the Visual Studio C runtime).
- //
- // Instead, we setup a SIGABRT handler, which is called in abort() right
- // before calling Windows Error Reporting. In the handler, we exit the
- // process just like abort() does.
- ASSERT_NE(SIG_ERR, signal(SIGABRT, ExitSignalAbortHandler));
-#endif
-}
-
-TEST(logging, CHECK) {
- ASSERT_DEATH({SuppressAbortUI(); CHECK(false);}, "Check failed: false ");
- CHECK(true);
-
- ASSERT_DEATH({SuppressAbortUI(); CHECK_EQ(0, 1);}, "Check failed: 0 == 1 ");
- CHECK_EQ(0, 0);
-
- ASSERT_DEATH({SuppressAbortUI(); CHECK_STREQ("foo", "bar");},
- R"(Check failed: "foo" == "bar")");
- CHECK_STREQ("foo", "foo");
-
- // Test whether CHECK() and CHECK_STREQ() have a dangling if with no else.
- bool flag = false;
- if (true)
- CHECK(true);
- else
- flag = true;
- EXPECT_FALSE(flag) << "CHECK macro probably has a dangling if with no else";
-
- flag = false;
- if (true)
- CHECK_STREQ("foo", "foo");
- else
- flag = true;
- EXPECT_FALSE(flag) << "CHECK_STREQ probably has a dangling if with no else";
-}
-
-TEST(logging, DCHECK) {
- if (android::base::kEnableDChecks) {
- ASSERT_DEATH({SuppressAbortUI(); DCHECK(false);}, "DCheck failed: false ");
- }
- DCHECK(true);
-
- if (android::base::kEnableDChecks) {
- ASSERT_DEATH({SuppressAbortUI(); DCHECK_EQ(0, 1);}, "DCheck failed: 0 == 1 ");
- }
- DCHECK_EQ(0, 0);
-
- if (android::base::kEnableDChecks) {
- ASSERT_DEATH({SuppressAbortUI(); DCHECK_STREQ("foo", "bar");},
- R"(DCheck failed: "foo" == "bar")");
- }
- DCHECK_STREQ("foo", "foo");
-
- // No testing whether we have a dangling else, possibly. That's inherent to the if (constexpr)
- // setup we intentionally chose to force type-checks of debug code even in release builds (so
- // we don't get more bit-rot).
-}
-
-
-#define CHECK_WOULD_LOG_DISABLED(severity) \
- static_assert(android::base::severity < android::base::FATAL, "Bad input"); \
- for (size_t i = static_cast<size_t>(android::base::severity) + 1; \
- i <= static_cast<size_t>(android::base::FATAL); \
- ++i) { \
- { \
- android::base::ScopedLogSeverity sls2(static_cast<android::base::LogSeverity>(i)); \
- EXPECT_FALSE(WOULD_LOG(severity)) << i; \
- } \
- { \
- android::base::ScopedLogSeverity sls2(static_cast<android::base::LogSeverity>(i)); \
- EXPECT_FALSE(WOULD_LOG(::android::base::severity)) << i; \
- } \
- } \
-
-#define CHECK_WOULD_LOG_ENABLED(severity) \
- for (size_t i = static_cast<size_t>(android::base::VERBOSE); \
- i <= static_cast<size_t>(android::base::severity); \
- ++i) { \
- { \
- android::base::ScopedLogSeverity sls2(static_cast<android::base::LogSeverity>(i)); \
- EXPECT_TRUE(WOULD_LOG(severity)) << i; \
- } \
- { \
- android::base::ScopedLogSeverity sls2(static_cast<android::base::LogSeverity>(i)); \
- EXPECT_TRUE(WOULD_LOG(::android::base::severity)) << i; \
- } \
- } \
-
-TEST(logging, WOULD_LOG_FATAL) {
- CHECK_WOULD_LOG_ENABLED(FATAL);
-}
-
-TEST(logging, WOULD_LOG_FATAL_WITHOUT_ABORT_enabled) {
- CHECK_WOULD_LOG_ENABLED(FATAL_WITHOUT_ABORT);
-}
-
-TEST(logging, WOULD_LOG_ERROR_disabled) {
- CHECK_WOULD_LOG_DISABLED(ERROR);
-}
-
-TEST(logging, WOULD_LOG_ERROR_enabled) {
- CHECK_WOULD_LOG_ENABLED(ERROR);
-}
-
-TEST(logging, WOULD_LOG_WARNING_disabled) {
- CHECK_WOULD_LOG_DISABLED(WARNING);
-}
-
-TEST(logging, WOULD_LOG_WARNING_enabled) {
- CHECK_WOULD_LOG_ENABLED(WARNING);
-}
-
-TEST(logging, WOULD_LOG_INFO_disabled) {
- CHECK_WOULD_LOG_DISABLED(INFO);
-}
-
-TEST(logging, WOULD_LOG_INFO_enabled) {
- CHECK_WOULD_LOG_ENABLED(INFO);
-}
-
-TEST(logging, WOULD_LOG_DEBUG_disabled) {
- CHECK_WOULD_LOG_DISABLED(DEBUG);
-}
-
-TEST(logging, WOULD_LOG_DEBUG_enabled) {
- CHECK_WOULD_LOG_ENABLED(DEBUG);
-}
-
-TEST(logging, WOULD_LOG_VERBOSE_disabled) {
- CHECK_WOULD_LOG_DISABLED(VERBOSE);
-}
-
-TEST(logging, WOULD_LOG_VERBOSE_enabled) {
- CHECK_WOULD_LOG_ENABLED(VERBOSE);
-}
-
-#undef CHECK_WOULD_LOG_DISABLED
-#undef CHECK_WOULD_LOG_ENABLED
-
-
-#if !defined(_WIN32)
-static std::string make_log_pattern(android::base::LogSeverity severity,
- const char* message) {
- static const char log_characters[] = "VDIWEFF";
- static_assert(arraysize(log_characters) - 1 == android::base::FATAL + 1,
- "Mismatch in size of log_characters and values in LogSeverity");
- char log_char = log_characters[severity];
- std::string holder(__FILE__);
- return android::base::StringPrintf(
- "%c \\d+-\\d+ \\d+:\\d+:\\d+ \\s*\\d+ \\s*\\d+ %s:\\d+] %s",
- log_char, basename(&holder[0]), message);
-}
-#endif
-
-static void CheckMessage(const std::string& output, android::base::LogSeverity severity,
- const char* expected, const char* expected_tag = nullptr) {
- // We can't usefully check the output of any of these on Windows because we
- // don't have std::regex, but we can at least make sure we printed at least as
- // many characters are in the log message.
- ASSERT_GT(output.length(), strlen(expected));
- ASSERT_NE(nullptr, strstr(output.c_str(), expected)) << output;
- if (expected_tag != nullptr) {
- ASSERT_NE(nullptr, strstr(output.c_str(), expected_tag)) << output;
- }
-
-#if !defined(_WIN32)
- std::string regex_str;
- if (expected_tag != nullptr) {
- regex_str.append(expected_tag);
- regex_str.append(" ");
- }
- regex_str.append(make_log_pattern(severity, expected));
- std::regex message_regex(regex_str);
- ASSERT_TRUE(std::regex_search(output, message_regex)) << output;
-#endif
-}
-
-static void CheckMessage(CapturedStderr& cap, android::base::LogSeverity severity,
- const char* expected, const char* expected_tag = nullptr) {
- cap.Stop();
- std::string output = cap.str();
- return CheckMessage(output, severity, expected, expected_tag);
-}
-
-#define CHECK_LOG_STREAM_DISABLED(severity) \
- { \
- android::base::ScopedLogSeverity sls1(android::base::FATAL); \
- CapturedStderr cap1; \
- LOG_STREAM(severity) << "foo bar"; \
- cap1.Stop(); \
- ASSERT_EQ("", cap1.str()); \
- } \
- { \
- android::base::ScopedLogSeverity sls1(android::base::FATAL); \
- CapturedStderr cap1; \
- LOG_STREAM(::android::base::severity) << "foo bar"; \
- cap1.Stop(); \
- ASSERT_EQ("", cap1.str()); \
- }
-
-#define CHECK_LOG_STREAM_ENABLED(severity) \
- { \
- android::base::ScopedLogSeverity sls2(android::base::severity); \
- CapturedStderr cap2; \
- LOG_STREAM(severity) << "foobar"; \
- CheckMessage(cap2, android::base::severity, "foobar"); \
- } \
- { \
- android::base::ScopedLogSeverity sls2(android::base::severity); \
- CapturedStderr cap2; \
- LOG_STREAM(::android::base::severity) << "foobar"; \
- CheckMessage(cap2, android::base::severity, "foobar"); \
- } \
-
-TEST(logging, LOG_STREAM_FATAL_WITHOUT_ABORT_enabled) {
- ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(FATAL_WITHOUT_ABORT));
-}
-
-TEST(logging, LOG_STREAM_ERROR_disabled) {
- CHECK_LOG_STREAM_DISABLED(ERROR);
-}
-
-TEST(logging, LOG_STREAM_ERROR_enabled) {
- ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(ERROR));
-}
-
-TEST(logging, LOG_STREAM_WARNING_disabled) {
- CHECK_LOG_STREAM_DISABLED(WARNING);
-}
-
-TEST(logging, LOG_STREAM_WARNING_enabled) {
- ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(WARNING));
-}
-
-TEST(logging, LOG_STREAM_INFO_disabled) {
- CHECK_LOG_STREAM_DISABLED(INFO);
-}
-
-TEST(logging, LOG_STREAM_INFO_enabled) {
- ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(INFO));
-}
-
-TEST(logging, LOG_STREAM_DEBUG_disabled) {
- CHECK_LOG_STREAM_DISABLED(DEBUG);
-}
-
-TEST(logging, LOG_STREAM_DEBUG_enabled) {
- ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(DEBUG));
-}
-
-TEST(logging, LOG_STREAM_VERBOSE_disabled) {
- CHECK_LOG_STREAM_DISABLED(VERBOSE);
-}
-
-TEST(logging, LOG_STREAM_VERBOSE_enabled) {
- ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(VERBOSE));
-}
-
-#undef CHECK_LOG_STREAM_DISABLED
-#undef CHECK_LOG_STREAM_ENABLED
-
-#define CHECK_LOG_DISABLED(severity) \
- { \
- android::base::ScopedLogSeverity sls1(android::base::FATAL); \
- CapturedStderr cap1; \
- LOG(severity) << "foo bar"; \
- cap1.Stop(); \
- ASSERT_EQ("", cap1.str()); \
- } \
- { \
- android::base::ScopedLogSeverity sls1(android::base::FATAL); \
- CapturedStderr cap1; \
- LOG(::android::base::severity) << "foo bar"; \
- cap1.Stop(); \
- ASSERT_EQ("", cap1.str()); \
- }
-
-#define CHECK_LOG_ENABLED(severity) \
- { \
- android::base::ScopedLogSeverity sls2(android::base::severity); \
- CapturedStderr cap2; \
- LOG(severity) << "foobar"; \
- CheckMessage(cap2, android::base::severity, "foobar"); \
- } \
- { \
- android::base::ScopedLogSeverity sls2(android::base::severity); \
- CapturedStderr cap2; \
- LOG(::android::base::severity) << "foobar"; \
- CheckMessage(cap2, android::base::severity, "foobar"); \
- } \
-
-TEST(logging, LOG_FATAL) {
- ASSERT_DEATH({SuppressAbortUI(); LOG(FATAL) << "foobar";}, "foobar");
- ASSERT_DEATH({SuppressAbortUI(); LOG(::android::base::FATAL) << "foobar";}, "foobar");
-}
-
-TEST(logging, LOG_FATAL_WITHOUT_ABORT_enabled) {
- ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(FATAL_WITHOUT_ABORT));
-}
-
-TEST(logging, LOG_ERROR_disabled) {
- CHECK_LOG_DISABLED(ERROR);
-}
-
-TEST(logging, LOG_ERROR_enabled) {
- ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(ERROR));
-}
-
-TEST(logging, LOG_WARNING_disabled) {
- CHECK_LOG_DISABLED(WARNING);
-}
-
-TEST(logging, LOG_WARNING_enabled) {
- ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(WARNING));
-}
-
-TEST(logging, LOG_INFO_disabled) {
- CHECK_LOG_DISABLED(INFO);
-}
-
-TEST(logging, LOG_INFO_enabled) {
- ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(INFO));
-}
-
-TEST(logging, LOG_DEBUG_disabled) {
- CHECK_LOG_DISABLED(DEBUG);
-}
-
-TEST(logging, LOG_DEBUG_enabled) {
- ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(DEBUG));
-}
-
-TEST(logging, LOG_VERBOSE_disabled) {
- CHECK_LOG_DISABLED(VERBOSE);
-}
-
-TEST(logging, LOG_VERBOSE_enabled) {
- ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(VERBOSE));
-}
-
-#undef CHECK_LOG_DISABLED
-#undef CHECK_LOG_ENABLED
-
-TEST(logging, LOG_complex_param) {
-#define CHECK_LOG_COMBINATION(use_scoped_log_severity_info, use_logging_severity_info) \
- { \
- android::base::ScopedLogSeverity sls( \
- (use_scoped_log_severity_info) ? ::android::base::INFO : ::android::base::WARNING); \
- CapturedStderr cap; \
- LOG((use_logging_severity_info) ? ::android::base::INFO : ::android::base::WARNING) \
- << "foobar"; \
- if ((use_scoped_log_severity_info) || !(use_logging_severity_info)) { \
- ASSERT_NO_FATAL_FAILURE(CheckMessage( \
- cap, (use_logging_severity_info) ? ::android::base::INFO : ::android::base::WARNING, \
- "foobar")); \
- } else { \
- cap.Stop(); \
- ASSERT_EQ("", cap.str()); \
- } \
- }
-
- CHECK_LOG_COMBINATION(false,false);
- CHECK_LOG_COMBINATION(false,true);
- CHECK_LOG_COMBINATION(true,false);
- CHECK_LOG_COMBINATION(true,true);
-
-#undef CHECK_LOG_COMBINATION
-}
-
-
-TEST(logging, LOG_does_not_clobber_errno) {
- CapturedStderr cap;
- errno = 12345;
- LOG(INFO) << (errno = 67890);
- EXPECT_EQ(12345, errno) << "errno was not restored";
-
- ASSERT_NO_FATAL_FAILURE(CheckMessage(cap, android::base::INFO, "67890"));
-}
-
-TEST(logging, PLOG_does_not_clobber_errno) {
- CapturedStderr cap;
- errno = 12345;
- PLOG(INFO) << (errno = 67890);
- EXPECT_EQ(12345, errno) << "errno was not restored";
-
- ASSERT_NO_FATAL_FAILURE(CheckMessage(cap, android::base::INFO, "67890"));
-}
-
-TEST(logging, LOG_does_not_have_dangling_if) {
- CapturedStderr cap; // So the logging below has no side-effects.
-
- // Do the test two ways: once where we hypothesize that LOG()'s if
- // will evaluate to true (when severity is high enough) and once when we
- // expect it to evaluate to false (when severity is not high enough).
- bool flag = false;
- if (true)
- LOG(INFO) << "foobar";
- else
- flag = true;
-
- EXPECT_FALSE(flag) << "LOG macro probably has a dangling if with no else";
-
- flag = false;
- if (true)
- LOG(VERBOSE) << "foobar";
- else
- flag = true;
-
- EXPECT_FALSE(flag) << "LOG macro probably has a dangling if with no else";
-}
-
-#define CHECK_PLOG_DISABLED(severity) \
- { \
- android::base::ScopedLogSeverity sls1(android::base::FATAL); \
- CapturedStderr cap1; \
- PLOG(severity) << "foo bar"; \
- cap1.Stop(); \
- ASSERT_EQ("", cap1.str()); \
- } \
- { \
- android::base::ScopedLogSeverity sls1(android::base::FATAL); \
- CapturedStderr cap1; \
- PLOG(severity) << "foo bar"; \
- cap1.Stop(); \
- ASSERT_EQ("", cap1.str()); \
- }
-
-#define CHECK_PLOG_ENABLED(severity) \
- { \
- android::base::ScopedLogSeverity sls2(android::base::severity); \
- CapturedStderr cap2; \
- errno = ENOENT; \
- PLOG(severity) << "foobar"; \
- CheckMessage(cap2, android::base::severity, "foobar: No such file or directory"); \
- } \
- { \
- android::base::ScopedLogSeverity sls2(android::base::severity); \
- CapturedStderr cap2; \
- errno = ENOENT; \
- PLOG(severity) << "foobar"; \
- CheckMessage(cap2, android::base::severity, "foobar: No such file or directory"); \
- } \
-
-TEST(logging, PLOG_FATAL) {
- ASSERT_DEATH({SuppressAbortUI(); PLOG(FATAL) << "foobar";}, "foobar");
- ASSERT_DEATH({SuppressAbortUI(); PLOG(::android::base::FATAL) << "foobar";}, "foobar");
-}
-
-TEST(logging, PLOG_FATAL_WITHOUT_ABORT_enabled) {
- ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(FATAL_WITHOUT_ABORT));
-}
-
-TEST(logging, PLOG_ERROR_disabled) {
- CHECK_PLOG_DISABLED(ERROR);
-}
-
-TEST(logging, PLOG_ERROR_enabled) {
- ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(ERROR));
-}
-
-TEST(logging, PLOG_WARNING_disabled) {
- CHECK_PLOG_DISABLED(WARNING);
-}
-
-TEST(logging, PLOG_WARNING_enabled) {
- ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(WARNING));
-}
-
-TEST(logging, PLOG_INFO_disabled) {
- CHECK_PLOG_DISABLED(INFO);
-}
-
-TEST(logging, PLOG_INFO_enabled) {
- ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(INFO));
-}
-
-TEST(logging, PLOG_DEBUG_disabled) {
- CHECK_PLOG_DISABLED(DEBUG);
-}
-
-TEST(logging, PLOG_DEBUG_enabled) {
- ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(DEBUG));
-}
-
-TEST(logging, PLOG_VERBOSE_disabled) {
- CHECK_PLOG_DISABLED(VERBOSE);
-}
-
-TEST(logging, PLOG_VERBOSE_enabled) {
- ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(VERBOSE));
-}
-
-#undef CHECK_PLOG_DISABLED
-#undef CHECK_PLOG_ENABLED
-
-
-TEST(logging, UNIMPLEMENTED) {
- std::string expected = android::base::StringPrintf("%s unimplemented ", __PRETTY_FUNCTION__);
-
- CapturedStderr cap;
- errno = ENOENT;
- UNIMPLEMENTED(ERROR);
- ASSERT_NO_FATAL_FAILURE(CheckMessage(cap, android::base::ERROR, expected.c_str()));
-}
-
-static void NoopAborter(const char* msg ATTRIBUTE_UNUSED) {
- LOG(ERROR) << "called noop";
-}
-
-TEST(logging, LOG_FATAL_NOOP_ABORTER) {
- CapturedStderr cap;
- {
- android::base::SetAborter(NoopAborter);
-
- android::base::ScopedLogSeverity sls(android::base::ERROR);
- LOG(FATAL) << "foobar";
- cap.Stop();
-
- android::base::SetAborter(android::base::DefaultAborter);
- }
- std::string output = cap.str();
- ASSERT_NO_FATAL_FAILURE(CheckMessage(output, android::base::FATAL, "foobar"));
- ASSERT_NO_FATAL_FAILURE(CheckMessage(output, android::base::ERROR, "called noop"));
-
- ASSERT_DEATH({SuppressAbortUI(); LOG(FATAL) << "foobar";}, "foobar");
-}
-
-struct CountLineAborter {
- static void CountLineAborterFunction(const char* msg) {
- while (*msg != 0) {
- if (*msg == '\n') {
- newline_count++;
- }
- msg++;
- }
- }
- static size_t newline_count;
-};
-size_t CountLineAborter::newline_count = 0;
-
-TEST(logging, LOG_FATAL_ABORTER_MESSAGE) {
- CountLineAborter::newline_count = 0;
- android::base::SetAborter(CountLineAborter::CountLineAborterFunction);
-
- android::base::ScopedLogSeverity sls(android::base::ERROR);
- CapturedStderr cap;
- LOG(FATAL) << "foo\nbar";
-
- EXPECT_EQ(CountLineAborter::newline_count, 1U + 1U); // +1 for final '\n'.
-}
-
-__attribute__((constructor)) void TestLoggingInConstructor() {
- LOG(ERROR) << "foobar";
-}
-
-TEST(logging, StdioLogger) {
- CapturedStderr cap_err;
- CapturedStdout cap_out;
- android::base::SetLogger(android::base::StdioLogger);
- LOG(INFO) << "out";
- LOG(ERROR) << "err";
- cap_err.Stop();
- cap_out.Stop();
-
- // For INFO we expect just the literal "out\n".
- ASSERT_EQ("out\n", cap_out.str());
- // Whereas ERROR logging includes the program name.
- ASSERT_EQ(android::base::Basename(android::base::GetExecutablePath()) + ": err\n", cap_err.str());
-}
diff --git a/base/macros_test.cpp b/base/macros_test.cpp
deleted file mode 100644
index 2b522db..0000000
--- a/base/macros_test.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/macros.h"
-
-#include <stdint.h>
-
-#include <gtest/gtest.h>
-
-TEST(macros, SIZEOF_MEMBER_macro) {
- struct S {
- int32_t i32;
- double d;
- };
- ASSERT_EQ(4U, SIZEOF_MEMBER(S, i32));
- ASSERT_EQ(8U, SIZEOF_MEMBER(S, d));
-}
diff --git a/base/mapped_file.cpp b/base/mapped_file.cpp
deleted file mode 100644
index fff3453..0000000
--- a/base/mapped_file.cpp
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/mapped_file.h"
-
-#include <utility>
-
-#include <errno.h>
-
-namespace android {
-namespace base {
-
-static constexpr char kEmptyBuffer[] = {'0'};
-
-static off64_t InitPageSize() {
-#if defined(_WIN32)
- SYSTEM_INFO si;
- GetSystemInfo(&si);
- return si.dwAllocationGranularity;
-#else
- return sysconf(_SC_PAGE_SIZE);
-#endif
-}
-
-std::unique_ptr<MappedFile> MappedFile::FromFd(borrowed_fd fd, off64_t offset, size_t length,
- int prot) {
-#if defined(_WIN32)
- return FromOsHandle(reinterpret_cast<HANDLE>(_get_osfhandle(fd.get())), offset, length, prot);
-#else
- return FromOsHandle(fd.get(), offset, length, prot);
-#endif
-}
-
-std::unique_ptr<MappedFile> MappedFile::FromOsHandle(os_handle h, off64_t offset, size_t length,
- int prot) {
- static const off64_t page_size = InitPageSize();
- size_t slop = offset % page_size;
- off64_t file_offset = offset - slop;
- off64_t file_length = length + slop;
-
-#if defined(_WIN32)
- HANDLE handle = CreateFileMappingW(
- h, nullptr, (prot & PROT_WRITE) ? PAGE_READWRITE : PAGE_READONLY, 0, 0, nullptr);
- if (handle == nullptr) {
- // http://b/119818070 "app crashes when reading asset of zero length".
- // Return a MappedFile that's only valid for reading the size.
- if (length == 0 && ::GetLastError() == ERROR_FILE_INVALID) {
- return std::unique_ptr<MappedFile>(
- new MappedFile(const_cast<char*>(kEmptyBuffer), 0, 0, nullptr));
- }
- return nullptr;
- }
- void* base = MapViewOfFile(handle, (prot & PROT_WRITE) ? FILE_MAP_ALL_ACCESS : FILE_MAP_READ, 0,
- file_offset, file_length);
- if (base == nullptr) {
- CloseHandle(handle);
- return nullptr;
- }
- return std::unique_ptr<MappedFile>(
- new MappedFile(static_cast<char*>(base), length, slop, handle));
-#else
- void* base = mmap(nullptr, file_length, prot, MAP_SHARED, h, file_offset);
- if (base == MAP_FAILED) {
- // http://b/119818070 "app crashes when reading asset of zero length".
- // mmap fails with EINVAL for a zero length region.
- if (errno == EINVAL && length == 0) {
- return std::unique_ptr<MappedFile>(new MappedFile(const_cast<char*>(kEmptyBuffer), 0, 0));
- }
- return nullptr;
- }
- return std::unique_ptr<MappedFile>(new MappedFile(static_cast<char*>(base), length, slop));
-#endif
-}
-
-MappedFile::MappedFile(MappedFile&& other)
- : base_(std::exchange(other.base_, nullptr)),
- size_(std::exchange(other.size_, 0)),
- offset_(std::exchange(other.offset_, 0))
-#ifdef _WIN32
- ,
- handle_(std::exchange(other.handle_, nullptr))
-#endif
-{
-}
-
-MappedFile& MappedFile::operator=(MappedFile&& other) {
- Close();
- base_ = std::exchange(other.base_, nullptr);
- size_ = std::exchange(other.size_, 0);
- offset_ = std::exchange(other.offset_, 0);
-#ifdef _WIN32
- handle_ = std::exchange(other.handle_, nullptr);
-#endif
- return *this;
-}
-
-MappedFile::~MappedFile() {
- Close();
-}
-
-void MappedFile::Close() {
-#if defined(_WIN32)
- if (base_ != nullptr && size_ != 0) UnmapViewOfFile(base_);
- if (handle_ != nullptr) CloseHandle(handle_);
- handle_ = nullptr;
-#else
- if (base_ != nullptr && size_ != 0) munmap(base_, size_ + offset_);
-#endif
-
- base_ = nullptr;
- offset_ = size_ = 0;
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/mapped_file_test.cpp b/base/mapped_file_test.cpp
deleted file mode 100644
index d21703c..0000000
--- a/base/mapped_file_test.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/mapped_file.h"
-
-#include <gtest/gtest.h>
-
-#include <errno.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-#include <string>
-
-#include "android-base/file.h"
-
-TEST(mapped_file, smoke) {
- TemporaryFile tf;
- ASSERT_TRUE(tf.fd != -1);
- ASSERT_TRUE(android::base::WriteStringToFd("hello world", tf.fd));
-
- auto m = android::base::MappedFile::FromFd(tf.fd, 3, 2, PROT_READ);
- ASSERT_EQ(2u, m->size());
- ASSERT_EQ('l', m->data()[0]);
- ASSERT_EQ('o', m->data()[1]);
-}
-
-TEST(mapped_file, zero_length_mapping) {
- // http://b/119818070 "app crashes when reading asset of zero length".
- // mmap fails with EINVAL for a zero length region.
- TemporaryFile tf;
- ASSERT_TRUE(tf.fd != -1);
-
- auto m = android::base::MappedFile::FromFd(tf.fd, 4096, 0, PROT_READ);
- EXPECT_EQ(0u, m->size());
- EXPECT_NE(nullptr, m->data());
-}
diff --git a/base/no_destructor_test.cpp b/base/no_destructor_test.cpp
deleted file mode 100644
index f19468a..0000000
--- a/base/no_destructor_test.cpp
+++ /dev/null
@@ -1,66 +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 "android-base/no_destructor.h"
-
-#include <gtest/gtest.h>
-
-struct __attribute__((packed)) Bomb {
- Bomb() : magic_(123) {}
-
- ~Bomb() { exit(42); }
-
- int get() const { return magic_; }
-
- private:
- [[maybe_unused]] char padding_;
- int magic_;
-};
-
-TEST(no_destructor, bomb) {
- ASSERT_EXIT(({
- {
- Bomb b;
- if (b.get() != 123) exit(1);
- }
-
- exit(0);
- }),
- ::testing::ExitedWithCode(42), "");
-}
-
-TEST(no_destructor, defused) {
- ASSERT_EXIT(({
- {
- android::base::NoDestructor<Bomb> b;
- if (b->get() != 123) exit(1);
- }
-
- exit(0);
- }),
- ::testing::ExitedWithCode(0), "");
-}
-
-TEST(no_destructor, operators) {
- android::base::NoDestructor<Bomb> b;
- const android::base::NoDestructor<Bomb>& c = b;
- ASSERT_EQ(123, b.get()->get());
- ASSERT_EQ(123, b->get());
- ASSERT_EQ(123, (*b).get());
- ASSERT_EQ(123, c.get()->get());
- ASSERT_EQ(123, c->get());
- ASSERT_EQ(123, (*c).get());
-}
diff --git a/base/parsebool.cpp b/base/parsebool.cpp
deleted file mode 100644
index ff96fe9..0000000
--- a/base/parsebool.cpp
+++ /dev/null
@@ -1,34 +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 "android-base/parsebool.h"
-#include <errno.h>
-
-namespace android {
-namespace base {
-
-ParseBoolResult ParseBool(std::string_view s) {
- if (s == "1" || s == "y" || s == "yes" || s == "on" || s == "true") {
- return ParseBoolResult::kTrue;
- }
- if (s == "0" || s == "n" || s == "no" || s == "off" || s == "false") {
- return ParseBoolResult::kFalse;
- }
- return ParseBoolResult::kError;
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/parsebool_test.cpp b/base/parsebool_test.cpp
deleted file mode 100644
index a081994..0000000
--- a/base/parsebool_test.cpp
+++ /dev/null
@@ -1,48 +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 "android-base/parsebool.h"
-
-#include <errno.h>
-
-#include <gtest/gtest.h>
-#include <string_view>
-
-using android::base::ParseBool;
-using android::base::ParseBoolResult;
-
-TEST(parsebool, true_) {
- static const char* yes[] = {
- "1", "on", "true", "y", "yes",
- };
- for (const char* s : yes) {
- ASSERT_EQ(ParseBoolResult::kTrue, ParseBool(s));
- }
-}
-
-TEST(parsebool, false_) {
- static const char* no[] = {
- "0", "false", "n", "no", "off",
- };
- for (const char* s : no) {
- ASSERT_EQ(ParseBoolResult::kFalse, ParseBool(s));
- }
-}
-
-TEST(parsebool, invalid) {
- ASSERT_EQ(ParseBoolResult::kError, ParseBool("blarg"));
- ASSERT_EQ(ParseBoolResult::kError, ParseBool(""));
-}
diff --git a/base/parsedouble_test.cpp b/base/parsedouble_test.cpp
deleted file mode 100644
index ec3c10c..0000000
--- a/base/parsedouble_test.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/parsedouble.h"
-
-#include <gtest/gtest.h>
-
-TEST(parsedouble, double_smoke) {
- double d;
- ASSERT_FALSE(android::base::ParseDouble("", &d));
- ASSERT_FALSE(android::base::ParseDouble("x", &d));
- ASSERT_FALSE(android::base::ParseDouble("123.4x", &d));
-
- ASSERT_TRUE(android::base::ParseDouble("123.4", &d));
- ASSERT_DOUBLE_EQ(123.4, d);
- ASSERT_TRUE(android::base::ParseDouble("-123.4", &d));
- ASSERT_DOUBLE_EQ(-123.4, d);
-
- ASSERT_TRUE(android::base::ParseDouble("0", &d, 0.0));
- ASSERT_DOUBLE_EQ(0.0, d);
- ASSERT_FALSE(android::base::ParseDouble("0", &d, 1e-9));
- ASSERT_FALSE(android::base::ParseDouble("3.0", &d, -1.0, 2.0));
- ASSERT_TRUE(android::base::ParseDouble("1.0", &d, 0.0, 2.0));
- ASSERT_DOUBLE_EQ(1.0, d);
-
- ASSERT_FALSE(android::base::ParseDouble("123.4x", nullptr));
- ASSERT_TRUE(android::base::ParseDouble("-123.4", nullptr));
- ASSERT_FALSE(android::base::ParseDouble("3.0", nullptr, -1.0, 2.0));
- ASSERT_TRUE(android::base::ParseDouble("1.0", nullptr, 0.0, 2.0));
-}
-
-TEST(parsedouble, float_smoke) {
- float f;
- ASSERT_FALSE(android::base::ParseFloat("", &f));
- ASSERT_FALSE(android::base::ParseFloat("x", &f));
- ASSERT_FALSE(android::base::ParseFloat("123.4x", &f));
-
- ASSERT_TRUE(android::base::ParseFloat("123.4", &f));
- ASSERT_FLOAT_EQ(123.4, f);
- ASSERT_TRUE(android::base::ParseFloat("-123.4", &f));
- ASSERT_FLOAT_EQ(-123.4, f);
-
- ASSERT_TRUE(android::base::ParseFloat("0", &f, 0.0));
- ASSERT_FLOAT_EQ(0.0, f);
- ASSERT_FALSE(android::base::ParseFloat("0", &f, 1e-9));
- ASSERT_FALSE(android::base::ParseFloat("3.0", &f, -1.0, 2.0));
- ASSERT_TRUE(android::base::ParseFloat("1.0", &f, 0.0, 2.0));
- ASSERT_FLOAT_EQ(1.0, f);
-
- ASSERT_FALSE(android::base::ParseFloat("123.4x", nullptr));
- ASSERT_TRUE(android::base::ParseFloat("-123.4", nullptr));
- ASSERT_FALSE(android::base::ParseFloat("3.0", nullptr, -1.0, 2.0));
- ASSERT_TRUE(android::base::ParseFloat("1.0", nullptr, 0.0, 2.0));
-}
diff --git a/base/parseint_test.cpp b/base/parseint_test.cpp
deleted file mode 100644
index e449c33..0000000
--- a/base/parseint_test.cpp
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/parseint.h"
-
-#include <errno.h>
-
-#include <gtest/gtest.h>
-
-TEST(parseint, signed_smoke) {
- errno = 0;
- int i = 0;
- ASSERT_FALSE(android::base::ParseInt("x", &i));
- ASSERT_EQ(EINVAL, errno);
- errno = 0;
- ASSERT_FALSE(android::base::ParseInt("123x", &i));
- ASSERT_EQ(EINVAL, errno);
-
- ASSERT_TRUE(android::base::ParseInt("123", &i));
- ASSERT_EQ(123, i);
- ASSERT_EQ(0, errno);
- i = 0;
- EXPECT_TRUE(android::base::ParseInt(" 123", &i));
- EXPECT_EQ(123, i);
- ASSERT_TRUE(android::base::ParseInt("-123", &i));
- ASSERT_EQ(-123, i);
- i = 0;
- EXPECT_TRUE(android::base::ParseInt(" -123", &i));
- EXPECT_EQ(-123, i);
-
- short s = 0;
- ASSERT_TRUE(android::base::ParseInt("1234", &s));
- ASSERT_EQ(1234, s);
-
- ASSERT_TRUE(android::base::ParseInt("12", &i, 0, 15));
- ASSERT_EQ(12, i);
- errno = 0;
- ASSERT_FALSE(android::base::ParseInt("-12", &i, 0, 15));
- ASSERT_EQ(ERANGE, errno);
- errno = 0;
- ASSERT_FALSE(android::base::ParseInt("16", &i, 0, 15));
- ASSERT_EQ(ERANGE, errno);
-
- errno = 0;
- ASSERT_FALSE(android::base::ParseInt<int>("x", nullptr));
- ASSERT_EQ(EINVAL, errno);
- errno = 0;
- ASSERT_FALSE(android::base::ParseInt<int>("123x", nullptr));
- ASSERT_EQ(EINVAL, errno);
- ASSERT_TRUE(android::base::ParseInt<int>("1234", nullptr));
-}
-
-TEST(parseint, unsigned_smoke) {
- errno = 0;
- unsigned int i = 0u;
- ASSERT_FALSE(android::base::ParseUint("x", &i));
- ASSERT_EQ(EINVAL, errno);
- errno = 0;
- ASSERT_FALSE(android::base::ParseUint("123x", &i));
- ASSERT_EQ(EINVAL, errno);
-
- ASSERT_TRUE(android::base::ParseUint("123", &i));
- ASSERT_EQ(123u, i);
- ASSERT_EQ(0, errno);
- i = 0u;
- EXPECT_TRUE(android::base::ParseUint(" 123", &i));
- EXPECT_EQ(123u, i);
- errno = 0;
- ASSERT_FALSE(android::base::ParseUint("-123", &i));
- EXPECT_EQ(EINVAL, errno);
- errno = 0;
- EXPECT_FALSE(android::base::ParseUint(" -123", &i));
- EXPECT_EQ(EINVAL, errno);
-
- unsigned short s = 0u;
- ASSERT_TRUE(android::base::ParseUint("1234", &s));
- ASSERT_EQ(1234u, s);
-
- ASSERT_TRUE(android::base::ParseUint("12", &i, 15u));
- ASSERT_EQ(12u, i);
- errno = 0;
- ASSERT_FALSE(android::base::ParseUint("-12", &i, 15u));
- ASSERT_EQ(EINVAL, errno);
- errno = 0;
- ASSERT_FALSE(android::base::ParseUint("16", &i, 15u));
- ASSERT_EQ(ERANGE, errno);
-
- errno = 0;
- ASSERT_FALSE(android::base::ParseUint<unsigned short>("x", nullptr));
- ASSERT_EQ(EINVAL, errno);
- errno = 0;
- ASSERT_FALSE(android::base::ParseUint<unsigned short>("123x", nullptr));
- ASSERT_EQ(EINVAL, errno);
- ASSERT_TRUE(android::base::ParseUint<unsigned short>("1234", nullptr));
-
- errno = 0;
- unsigned long long int lli;
- EXPECT_FALSE(android::base::ParseUint("-123", &lli));
- EXPECT_EQ(EINVAL, errno);
- errno = 0;
- EXPECT_FALSE(android::base::ParseUint(" -123", &lli));
- EXPECT_EQ(EINVAL, errno);
-}
-
-TEST(parseint, no_implicit_octal) {
- int i = 0;
- ASSERT_TRUE(android::base::ParseInt("0123", &i));
- ASSERT_EQ(123, i);
-
- unsigned int u = 0u;
- ASSERT_TRUE(android::base::ParseUint("0123", &u));
- ASSERT_EQ(123u, u);
-}
-
-TEST(parseint, explicit_hex) {
- int i = 0;
- ASSERT_TRUE(android::base::ParseInt("0x123", &i));
- ASSERT_EQ(0x123, i);
- i = 0;
- EXPECT_TRUE(android::base::ParseInt(" 0x123", &i));
- EXPECT_EQ(0x123, i);
-
- unsigned int u = 0u;
- ASSERT_TRUE(android::base::ParseUint("0x123", &u));
- ASSERT_EQ(0x123u, u);
- u = 0u;
- EXPECT_TRUE(android::base::ParseUint(" 0x123", &u));
- EXPECT_EQ(0x123u, u);
-}
-
-TEST(parseint, string) {
- int i = 0;
- ASSERT_TRUE(android::base::ParseInt(std::string("123"), &i));
- ASSERT_EQ(123, i);
-
- unsigned int u = 0u;
- ASSERT_TRUE(android::base::ParseUint(std::string("123"), &u));
- ASSERT_EQ(123u, u);
-}
-
-TEST(parseint, untouched_on_failure) {
- int i = 123;
- ASSERT_FALSE(android::base::ParseInt("456x", &i));
- ASSERT_EQ(123, i);
-
- unsigned int u = 123u;
- ASSERT_FALSE(android::base::ParseUint("456x", &u));
- ASSERT_EQ(123u, u);
-}
-
-TEST(parseint, ParseByteCount) {
- uint64_t i = 0;
- ASSERT_TRUE(android::base::ParseByteCount("123b", &i));
- ASSERT_EQ(123ULL, i);
-
- ASSERT_TRUE(android::base::ParseByteCount("8k", &i));
- ASSERT_EQ(8ULL * 1024, i);
-
- ASSERT_TRUE(android::base::ParseByteCount("8M", &i));
- ASSERT_EQ(8ULL * 1024 * 1024, i);
-
- ASSERT_TRUE(android::base::ParseByteCount("6g", &i));
- ASSERT_EQ(6ULL * 1024 * 1024 * 1024, i);
-
- ASSERT_TRUE(android::base::ParseByteCount("1T", &i));
- ASSERT_EQ(1ULL * 1024 * 1024 * 1024 * 1024, i);
-
- ASSERT_TRUE(android::base::ParseByteCount("2p", &i));
- ASSERT_EQ(2ULL * 1024 * 1024 * 1024 * 1024 * 1024, i);
-
- ASSERT_TRUE(android::base::ParseByteCount("4e", &i));
- ASSERT_EQ(4ULL * 1024 * 1024 * 1024 * 1024 * 1024 * 1024, i);
-}
-
-TEST(parseint, ParseByteCount_invalid_suffix) {
- unsigned u;
- ASSERT_FALSE(android::base::ParseByteCount("1x", &u));
-}
-
-TEST(parseint, ParseByteCount_overflow) {
- uint64_t u64;
- ASSERT_FALSE(android::base::ParseByteCount("4294967295E", &u64));
-
- uint16_t u16;
- ASSERT_TRUE(android::base::ParseByteCount("63k", &u16));
- ASSERT_EQ(63U * 1024, u16);
- ASSERT_TRUE(android::base::ParseByteCount("65535b", &u16));
- ASSERT_EQ(65535U, u16);
- ASSERT_FALSE(android::base::ParseByteCount("65k", &u16));
-}
diff --git a/base/parsenetaddress.cpp b/base/parsenetaddress.cpp
deleted file mode 100644
index dd80f6d..0000000
--- a/base/parsenetaddress.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/parsenetaddress.h"
-
-#include <algorithm>
-
-#include "android-base/stringprintf.h"
-#include "android-base/strings.h"
-
-namespace android {
-namespace base {
-
-bool ParseNetAddress(const std::string& address, std::string* host, int* port,
- std::string* canonical_address, std::string* error) {
- host->clear();
-
- bool ipv6 = true;
- bool saw_port = false;
- size_t colons = std::count(address.begin(), address.end(), ':');
- size_t dots = std::count(address.begin(), address.end(), '.');
- std::string port_str;
- if (address[0] == '[') {
- // [::1]:123
- if (address.rfind("]:") == std::string::npos) {
- *error = StringPrintf("bad IPv6 address '%s'", address.c_str());
- return false;
- }
- *host = address.substr(1, (address.find("]:") - 1));
- port_str = address.substr(address.rfind("]:") + 2);
- saw_port = true;
- } else if (dots == 0 && colons >= 2 && colons <= 7) {
- // ::1
- *host = address;
- } else if (colons <= 1) {
- // 1.2.3.4 or some.accidental.domain.com
- ipv6 = false;
- std::vector<std::string> pieces = Split(address, ":");
- *host = pieces[0];
- if (pieces.size() > 1) {
- port_str = pieces[1];
- saw_port = true;
- }
- }
-
- if (host->empty()) {
- *error = StringPrintf("no host in '%s'", address.c_str());
- return false;
- }
-
- if (saw_port) {
- if (sscanf(port_str.c_str(), "%d", port) != 1 || *port <= 0 ||
- *port > 65535) {
- *error = StringPrintf("bad port number '%s' in '%s'", port_str.c_str(),
- address.c_str());
- return false;
- }
- }
-
- if (canonical_address != nullptr) {
- *canonical_address =
- StringPrintf(ipv6 ? "[%s]:%d" : "%s:%d", host->c_str(), *port);
- }
-
- return true;
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/parsenetaddress_test.cpp b/base/parsenetaddress_test.cpp
deleted file mode 100644
index a3bfac8..0000000
--- a/base/parsenetaddress_test.cpp
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/parsenetaddress.h"
-
-#include <gtest/gtest.h>
-
-using android::base::ParseNetAddress;
-
-TEST(ParseNetAddressTest, TestUrl) {
- std::string canonical, host, error;
- int port = 123;
-
- EXPECT_TRUE(
- ParseNetAddress("www.google.com", &host, &port, &canonical, &error));
- EXPECT_EQ("www.google.com:123", canonical);
- EXPECT_EQ("www.google.com", host);
- EXPECT_EQ(123, port);
-
- EXPECT_TRUE(
- ParseNetAddress("www.google.com:666", &host, &port, &canonical, &error));
- EXPECT_EQ("www.google.com:666", canonical);
- EXPECT_EQ("www.google.com", host);
- EXPECT_EQ(666, port);
-}
-
-TEST(ParseNetAddressTest, TestIpv4) {
- std::string canonical, host, error;
- int port = 123;
-
- EXPECT_TRUE(ParseNetAddress("1.2.3.4", &host, &port, &canonical, &error));
- EXPECT_EQ("1.2.3.4:123", canonical);
- EXPECT_EQ("1.2.3.4", host);
- EXPECT_EQ(123, port);
-
- EXPECT_TRUE(ParseNetAddress("1.2.3.4:666", &host, &port, &canonical, &error));
- EXPECT_EQ("1.2.3.4:666", canonical);
- EXPECT_EQ("1.2.3.4", host);
- EXPECT_EQ(666, port);
-}
-
-TEST(ParseNetAddressTest, TestIpv6) {
- std::string canonical, host, error;
- int port = 123;
-
- EXPECT_TRUE(ParseNetAddress("::1", &host, &port, &canonical, &error));
- EXPECT_EQ("[::1]:123", canonical);
- EXPECT_EQ("::1", host);
- EXPECT_EQ(123, port);
-
- EXPECT_TRUE(ParseNetAddress("fe80::200:5aee:feaa:20a2", &host, &port,
- &canonical, &error));
- EXPECT_EQ("[fe80::200:5aee:feaa:20a2]:123", canonical);
- EXPECT_EQ("fe80::200:5aee:feaa:20a2", host);
- EXPECT_EQ(123, port);
-
- EXPECT_TRUE(ParseNetAddress("[::1]:666", &host, &port, &canonical, &error));
- EXPECT_EQ("[::1]:666", canonical);
- EXPECT_EQ("::1", host);
- EXPECT_EQ(666, port);
-
- EXPECT_TRUE(ParseNetAddress("[fe80::200:5aee:feaa:20a2]:666", &host, &port,
- &canonical, &error));
- EXPECT_EQ("[fe80::200:5aee:feaa:20a2]:666", canonical);
- EXPECT_EQ("fe80::200:5aee:feaa:20a2", host);
- EXPECT_EQ(666, port);
-}
-
-TEST(ParseNetAddressTest, TestInvalidAddress) {
- std::string canonical, host;
- int port;
-
- std::string failure_cases[] = {
- // Invalid IPv4.
- "1.2.3.4:",
- "1.2.3.4::",
- ":123",
-
- // Invalid IPv6.
- ":1",
- "::::::::1",
- "[::1",
- "[::1]",
- "[::1]:",
- "[::1]::",
-
- // Invalid port.
- "1.2.3.4:-1",
- "1.2.3.4:0",
- "1.2.3.4:65536"
- "1.2.3.4:hello",
- "[::1]:-1",
- "[::1]:0",
- "[::1]:65536",
- "[::1]:hello",
- };
-
- for (const auto& address : failure_cases) {
- // Failure should give some non-empty error string.
- std::string error;
- EXPECT_FALSE(ParseNetAddress(address, &host, &port, &canonical, &error));
- EXPECT_NE("", error);
- }
-}
-
-// Null canonical address argument.
-TEST(ParseNetAddressTest, TestNullCanonicalAddress) {
- std::string host, error;
- int port = 42;
-
- EXPECT_TRUE(ParseNetAddress("www.google.com", &host, &port, nullptr, &error));
- EXPECT_TRUE(ParseNetAddress("1.2.3.4", &host, &port, nullptr, &error));
- EXPECT_TRUE(ParseNetAddress("::1", &host, &port, nullptr, &error));
-}
diff --git a/base/process.cpp b/base/process.cpp
deleted file mode 100644
index b8cabf6..0000000
--- a/base/process.cpp
+++ /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.
- */
-
-#include "android-base/process.h"
-
-namespace android {
-namespace base {
-
-void AllPids::PidIterator::Increment() {
- if (!dir_) {
- return;
- }
-
- dirent* de;
- while ((de = readdir(dir_.get())) != nullptr) {
- pid_t pid = atoi(de->d_name);
- if (pid != 0) {
- pid_ = pid;
- return;
- }
- }
- pid_ = -1;
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/process_test.cpp b/base/process_test.cpp
deleted file mode 100644
index 056f667..0000000
--- a/base/process_test.cpp
+++ /dev/null
@@ -1,35 +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 "android-base/process.h"
-
-#include <unistd.h>
-
-#include <gtest/gtest.h>
-
-TEST(process, find_ourselves) {
-#if defined(__linux__)
- bool found_our_pid = false;
- for (const auto& pid : android::base::AllPids{}) {
- if (pid == getpid()) {
- found_our_pid = true;
- }
- }
-
- EXPECT_TRUE(found_our_pid);
-
-#endif
-}
diff --git a/base/properties.cpp b/base/properties.cpp
deleted file mode 100644
index 35e41a8..0000000
--- a/base/properties.cpp
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/properties.h"
-
-#if defined(__BIONIC__)
-#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
-#include <sys/system_properties.h>
-#include <sys/_system_properties.h>
-#endif
-
-#include <algorithm>
-#include <chrono>
-#include <limits>
-#include <map>
-#include <string>
-
-#include <android-base/parsebool.h>
-#include <android-base/parseint.h>
-#include <android-base/strings.h>
-
-namespace android {
-namespace base {
-
-bool GetBoolProperty(const std::string& key, bool default_value) {
- switch (ParseBool(GetProperty(key, ""))) {
- case ParseBoolResult::kError:
- return default_value;
- case ParseBoolResult::kFalse:
- return false;
- case ParseBoolResult::kTrue:
- return true;
- }
- __builtin_unreachable();
-}
-
-template <typename T>
-T GetIntProperty(const std::string& key, T default_value, T min, T max) {
- T result;
- std::string value = GetProperty(key, "");
- if (!value.empty() && android::base::ParseInt(value, &result, min, max)) return result;
- return default_value;
-}
-
-template <typename T>
-T GetUintProperty(const std::string& key, T default_value, T max) {
- T result;
- std::string value = GetProperty(key, "");
- if (!value.empty() && android::base::ParseUint(value, &result, max)) return result;
- return default_value;
-}
-
-template int8_t GetIntProperty(const std::string&, int8_t, int8_t, int8_t);
-template int16_t GetIntProperty(const std::string&, int16_t, int16_t, int16_t);
-template int32_t GetIntProperty(const std::string&, int32_t, int32_t, int32_t);
-template int64_t GetIntProperty(const std::string&, int64_t, int64_t, int64_t);
-
-template uint8_t GetUintProperty(const std::string&, uint8_t, uint8_t);
-template uint16_t GetUintProperty(const std::string&, uint16_t, uint16_t);
-template uint32_t GetUintProperty(const std::string&, uint32_t, uint32_t);
-template uint64_t GetUintProperty(const std::string&, uint64_t, uint64_t);
-
-#if !defined(__BIONIC__)
-static std::map<std::string, std::string>& g_properties = *new std::map<std::string, std::string>;
-static int __system_property_set(const char* key, const char* value) {
- g_properties[key] = value;
- return 0;
-}
-#endif
-
-std::string GetProperty(const std::string& key, const std::string& default_value) {
- std::string property_value;
-#if defined(__BIONIC__)
- const prop_info* pi = __system_property_find(key.c_str());
- if (pi == nullptr) return default_value;
-
- __system_property_read_callback(pi,
- [](void* cookie, const char*, const char* value, unsigned) {
- auto property_value = reinterpret_cast<std::string*>(cookie);
- *property_value = value;
- },
- &property_value);
-#else
- auto it = g_properties.find(key);
- if (it == g_properties.end()) return default_value;
- property_value = it->second;
-#endif
- // If the property exists but is empty, also return the default value.
- // Since we can't remove system properties, "empty" is traditionally
- // the same as "missing" (this was true for cutils' property_get).
- return property_value.empty() ? default_value : property_value;
-}
-
-bool SetProperty(const std::string& key, const std::string& value) {
- return (__system_property_set(key.c_str(), value.c_str()) == 0);
-}
-
-#if defined(__BIONIC__)
-
-struct WaitForPropertyData {
- bool done;
- const std::string* expected_value;
- unsigned last_read_serial;
-};
-
-static void WaitForPropertyCallback(void* data_ptr, const char*, const char* value, unsigned serial) {
- WaitForPropertyData* data = reinterpret_cast<WaitForPropertyData*>(data_ptr);
- if (*data->expected_value == value) {
- data->done = true;
- } else {
- data->last_read_serial = serial;
- }
-}
-
-// TODO: chrono_utils?
-static void DurationToTimeSpec(timespec& ts, const std::chrono::milliseconds d) {
- auto s = std::chrono::duration_cast<std::chrono::seconds>(d);
- auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(d - s);
- ts.tv_sec = std::min<std::chrono::seconds::rep>(s.count(), std::numeric_limits<time_t>::max());
- ts.tv_nsec = ns.count();
-}
-
-// TODO: boot_clock?
-using AbsTime = std::chrono::time_point<std::chrono::steady_clock>;
-
-static void UpdateTimeSpec(timespec& ts, std::chrono::milliseconds relative_timeout,
- const AbsTime& start_time) {
- auto now = std::chrono::steady_clock::now();
- auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
- if (time_elapsed >= relative_timeout) {
- ts = { 0, 0 };
- } else {
- auto remaining_timeout = relative_timeout - time_elapsed;
- DurationToTimeSpec(ts, remaining_timeout);
- }
-}
-
-// Waits for the system property `key` to be created.
-// Times out after `relative_timeout`.
-// Sets absolute_timeout which represents absolute time for the timeout.
-// Returns nullptr on timeout.
-static const prop_info* WaitForPropertyCreation(const std::string& key,
- const std::chrono::milliseconds& relative_timeout,
- const AbsTime& start_time) {
- // Find the property's prop_info*.
- const prop_info* pi;
- unsigned global_serial = 0;
- while ((pi = __system_property_find(key.c_str())) == nullptr) {
- // The property doesn't even exist yet.
- // Wait for a global change and then look again.
- timespec ts;
- UpdateTimeSpec(ts, relative_timeout, start_time);
- if (!__system_property_wait(nullptr, global_serial, &global_serial, &ts)) return nullptr;
- }
- return pi;
-}
-
-bool WaitForProperty(const std::string& key, const std::string& expected_value,
- std::chrono::milliseconds relative_timeout) {
- auto start_time = std::chrono::steady_clock::now();
- const prop_info* pi = WaitForPropertyCreation(key, relative_timeout, start_time);
- if (pi == nullptr) return false;
-
- WaitForPropertyData data;
- data.expected_value = &expected_value;
- data.done = false;
- while (true) {
- timespec ts;
- // Check whether the property has the value we're looking for?
- __system_property_read_callback(pi, WaitForPropertyCallback, &data);
- if (data.done) return true;
-
- // It didn't, so wait for the property to change before checking again.
- UpdateTimeSpec(ts, relative_timeout, start_time);
- uint32_t unused;
- if (!__system_property_wait(pi, data.last_read_serial, &unused, &ts)) return false;
- }
-}
-
-bool WaitForPropertyCreation(const std::string& key,
- std::chrono::milliseconds relative_timeout) {
- auto start_time = std::chrono::steady_clock::now();
- return (WaitForPropertyCreation(key, relative_timeout, start_time) != nullptr);
-}
-
-CachedProperty::CachedProperty(const char* property_name)
- : property_name_(property_name),
- prop_info_(nullptr),
- cached_area_serial_(0),
- cached_property_serial_(0),
- is_read_only_(android::base::StartsWith(property_name, "ro.")),
- read_only_property_(nullptr) {
- static_assert(sizeof(cached_value_) == PROP_VALUE_MAX);
-}
-
-const char* CachedProperty::Get(bool* changed) {
- std::optional<uint32_t> initial_property_serial_ = cached_property_serial_;
-
- // Do we have a `struct prop_info` yet?
- if (prop_info_ == nullptr) {
- // `__system_property_find` is expensive, so only retry if a property
- // has been created since last time we checked.
- uint32_t property_area_serial = __system_property_area_serial();
- if (property_area_serial != cached_area_serial_) {
- prop_info_ = __system_property_find(property_name_.c_str());
- cached_area_serial_ = property_area_serial;
- }
- }
-
- if (prop_info_ != nullptr) {
- // Only bother re-reading the property if it's actually changed since last time.
- uint32_t property_serial = __system_property_serial(prop_info_);
- if (property_serial != cached_property_serial_) {
- __system_property_read_callback(
- prop_info_,
- [](void* data, const char*, const char* value, uint32_t serial) {
- CachedProperty* instance = reinterpret_cast<CachedProperty*>(data);
- instance->cached_property_serial_ = serial;
- // Read only properties can be larger than PROP_VALUE_MAX, but also never change value
- // or location, thus we return the pointer from the shared memory directly.
- if (instance->is_read_only_) {
- instance->read_only_property_ = value;
- } else {
- strlcpy(instance->cached_value_, value, PROP_VALUE_MAX);
- }
- },
- this);
- }
- }
-
- if (changed) {
- *changed = cached_property_serial_ != initial_property_serial_;
- }
-
- if (is_read_only_) {
- return read_only_property_;
- } else {
- return cached_value_;
- }
-}
-
-#endif
-
-} // namespace base
-} // namespace android
diff --git a/base/properties_test.cpp b/base/properties_test.cpp
deleted file mode 100644
index c30c41e..0000000
--- a/base/properties_test.cpp
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/properties.h"
-
-#include <gtest/gtest.h>
-
-#include <atomic>
-#include <chrono>
-#include <string>
-#include <thread>
-
-#if !defined(_WIN32)
-using namespace std::literals;
-#endif
-
-TEST(properties, smoke) {
- android::base::SetProperty("debug.libbase.property_test", "hello");
-
- std::string s = android::base::GetProperty("debug.libbase.property_test", "");
- ASSERT_EQ("hello", s);
-
- android::base::SetProperty("debug.libbase.property_test", "world");
- s = android::base::GetProperty("debug.libbase.property_test", "");
- ASSERT_EQ("world", s);
-
- s = android::base::GetProperty("this.property.does.not.exist", "");
- ASSERT_EQ("", s);
-
- s = android::base::GetProperty("this.property.does.not.exist", "default");
- ASSERT_EQ("default", s);
-}
-
-TEST(properties, empty) {
- // Because you can't delete a property, people "delete" them by
- // setting them to the empty string. In that case we'd want to
- // keep the default value (like cutils' property_get did).
- android::base::SetProperty("debug.libbase.property_test", "");
- std::string s = android::base::GetProperty("debug.libbase.property_test", "default");
- ASSERT_EQ("default", s);
-}
-
-static void CheckGetBoolProperty(bool expected, const std::string& value, bool default_value) {
- android::base::SetProperty("debug.libbase.property_test", value.c_str());
- ASSERT_EQ(expected, android::base::GetBoolProperty("debug.libbase.property_test", default_value));
-}
-
-TEST(properties, GetBoolProperty_true) {
- CheckGetBoolProperty(true, "1", false);
- CheckGetBoolProperty(true, "y", false);
- CheckGetBoolProperty(true, "yes", false);
- CheckGetBoolProperty(true, "on", false);
- CheckGetBoolProperty(true, "true", false);
-}
-
-TEST(properties, GetBoolProperty_false) {
- CheckGetBoolProperty(false, "0", true);
- CheckGetBoolProperty(false, "n", true);
- CheckGetBoolProperty(false, "no", true);
- CheckGetBoolProperty(false, "off", true);
- CheckGetBoolProperty(false, "false", true);
-}
-
-TEST(properties, GetBoolProperty_default) {
- CheckGetBoolProperty(true, "burp", true);
- CheckGetBoolProperty(false, "burp", false);
-}
-
-template <typename T> void CheckGetIntProperty() {
- // Positive and negative.
- android::base::SetProperty("debug.libbase.property_test", "-12");
- EXPECT_EQ(T(-12), android::base::GetIntProperty<T>("debug.libbase.property_test", 45));
- android::base::SetProperty("debug.libbase.property_test", "12");
- EXPECT_EQ(T(12), android::base::GetIntProperty<T>("debug.libbase.property_test", 45));
-
- // Default value.
- android::base::SetProperty("debug.libbase.property_test", "");
- EXPECT_EQ(T(45), android::base::GetIntProperty<T>("debug.libbase.property_test", 45));
-
- // Bounds checks.
- android::base::SetProperty("debug.libbase.property_test", "0");
- EXPECT_EQ(T(45), android::base::GetIntProperty<T>("debug.libbase.property_test", 45, 1, 2));
- android::base::SetProperty("debug.libbase.property_test", "1");
- EXPECT_EQ(T(1), android::base::GetIntProperty<T>("debug.libbase.property_test", 45, 1, 2));
- android::base::SetProperty("debug.libbase.property_test", "2");
- EXPECT_EQ(T(2), android::base::GetIntProperty<T>("debug.libbase.property_test", 45, 1, 2));
- android::base::SetProperty("debug.libbase.property_test", "3");
- EXPECT_EQ(T(45), android::base::GetIntProperty<T>("debug.libbase.property_test", 45, 1, 2));
-}
-
-template <typename T> void CheckGetUintProperty() {
- // Positive.
- android::base::SetProperty("debug.libbase.property_test", "12");
- EXPECT_EQ(T(12), android::base::GetUintProperty<T>("debug.libbase.property_test", 45));
-
- // Default value.
- android::base::SetProperty("debug.libbase.property_test", "");
- EXPECT_EQ(T(45), android::base::GetUintProperty<T>("debug.libbase.property_test", 45));
-
- // Bounds checks.
- android::base::SetProperty("debug.libbase.property_test", "12");
- EXPECT_EQ(T(12), android::base::GetUintProperty<T>("debug.libbase.property_test", 33, 22));
- android::base::SetProperty("debug.libbase.property_test", "12");
- EXPECT_EQ(T(5), android::base::GetUintProperty<T>("debug.libbase.property_test", 5, 10));
-}
-
-TEST(properties, GetIntProperty_int8_t) { CheckGetIntProperty<int8_t>(); }
-TEST(properties, GetIntProperty_int16_t) { CheckGetIntProperty<int16_t>(); }
-TEST(properties, GetIntProperty_int32_t) { CheckGetIntProperty<int32_t>(); }
-TEST(properties, GetIntProperty_int64_t) { CheckGetIntProperty<int64_t>(); }
-
-TEST(properties, GetUintProperty_uint8_t) { CheckGetUintProperty<uint8_t>(); }
-TEST(properties, GetUintProperty_uint16_t) { CheckGetUintProperty<uint16_t>(); }
-TEST(properties, GetUintProperty_uint32_t) { CheckGetUintProperty<uint32_t>(); }
-TEST(properties, GetUintProperty_uint64_t) { CheckGetUintProperty<uint64_t>(); }
-
-TEST(properties, WaitForProperty) {
-#if defined(__BIONIC__)
- std::atomic<bool> flag{false};
- std::thread thread([&]() {
- std::this_thread::sleep_for(100ms);
- android::base::SetProperty("debug.libbase.WaitForProperty_test", "a");
- while (!flag) std::this_thread::yield();
- android::base::SetProperty("debug.libbase.WaitForProperty_test", "b");
- });
-
- ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "a", 1s));
- flag = true;
- ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b", 1s));
- thread.join();
-#else
- GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
-#endif
-}
-
-TEST(properties, WaitForProperty_timeout) {
-#if defined(__BIONIC__)
- auto t0 = std::chrono::steady_clock::now();
- ASSERT_FALSE(android::base::WaitForProperty("debug.libbase.WaitForProperty_timeout_test", "a",
- 200ms));
- auto t1 = std::chrono::steady_clock::now();
-
- ASSERT_GE(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 200ms);
- // Upper bounds on timing are inherently flaky, but let's try...
- ASSERT_LT(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 600ms);
-#else
- GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
-#endif
-}
-
-TEST(properties, WaitForProperty_MaxTimeout) {
-#if defined(__BIONIC__)
- std::atomic<bool> flag{false};
- std::thread thread([&]() {
- android::base::SetProperty("debug.libbase.WaitForProperty_test", "a");
- while (!flag) std::this_thread::yield();
- std::this_thread::sleep_for(500ms);
- android::base::SetProperty("debug.libbase.WaitForProperty_test", "b");
- });
-
- ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "a", 1s));
- flag = true;
- // Test that this does not immediately return false due to overflow issues with the timeout.
- ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b"));
- thread.join();
-#else
- GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
-#endif
-}
-
-TEST(properties, WaitForProperty_NegativeTimeout) {
-#if defined(__BIONIC__)
- std::atomic<bool> flag{false};
- std::thread thread([&]() {
- android::base::SetProperty("debug.libbase.WaitForProperty_test", "a");
- while (!flag) std::this_thread::yield();
- std::this_thread::sleep_for(500ms);
- android::base::SetProperty("debug.libbase.WaitForProperty_test", "b");
- });
-
- ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "a", 1s));
- flag = true;
- // Assert that this immediately returns with a negative timeout
- ASSERT_FALSE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b", -100ms));
- thread.join();
-#else
- GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
-#endif
-}
-
-TEST(properties, WaitForPropertyCreation) {
-#if defined(__BIONIC__)
- std::thread thread([&]() {
- std::this_thread::sleep_for(100ms);
- android::base::SetProperty("debug.libbase.WaitForPropertyCreation_test", "a");
- });
-
- ASSERT_TRUE(android::base::WaitForPropertyCreation(
- "debug.libbase.WaitForPropertyCreation_test", 1s));
- thread.join();
-#else
- GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
-#endif
-}
-
-TEST(properties, WaitForPropertyCreation_timeout) {
-#if defined(__BIONIC__)
- auto t0 = std::chrono::steady_clock::now();
- ASSERT_FALSE(android::base::WaitForPropertyCreation(
- "debug.libbase.WaitForPropertyCreation_timeout_test", 200ms));
- auto t1 = std::chrono::steady_clock::now();
-
- ASSERT_GE(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 200ms);
- // Upper bounds on timing are inherently flaky, but let's try...
- ASSERT_LT(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 600ms);
-#else
- GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
-#endif
-}
-
-TEST(properties, CachedProperty) {
-#if defined(__BIONIC__)
- android::base::CachedProperty cached_property("debug.libbase.CachedProperty_test");
- bool changed;
- cached_property.Get(&changed);
-
- android::base::SetProperty("debug.libbase.CachedProperty_test", "foo");
- ASSERT_STREQ("foo", cached_property.Get(&changed));
- ASSERT_TRUE(changed);
-
- ASSERT_STREQ("foo", cached_property.Get(&changed));
- ASSERT_FALSE(changed);
-
- android::base::SetProperty("debug.libbase.CachedProperty_test", "bar");
- ASSERT_STREQ("bar", cached_property.Get(&changed));
- ASSERT_TRUE(changed);
-
- ASSERT_STREQ("bar", cached_property.Get(&changed));
- ASSERT_FALSE(changed);
-
-#else
- GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
-#endif
-}
diff --git a/base/result_test.cpp b/base/result_test.cpp
deleted file mode 100644
index c0ac0fd..0000000
--- a/base/result_test.cpp
+++ /dev/null
@@ -1,422 +0,0 @@
-/*
- * 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.
- */
-
-#include "android-base/result.h"
-
-#include "errno.h"
-
-#include <istream>
-#include <string>
-
-#include <gtest/gtest.h>
-
-using namespace std::string_literals;
-
-namespace android {
-namespace base {
-
-TEST(result, result_accessors) {
- Result<std::string> result = "success";
- ASSERT_RESULT_OK(result);
- ASSERT_TRUE(result.has_value());
-
- EXPECT_EQ("success", *result);
- EXPECT_EQ("success", result.value());
-
- EXPECT_EQ('s', result->data()[0]);
-}
-
-TEST(result, result_accessors_rvalue) {
- ASSERT_TRUE(Result<std::string>("success").ok());
- ASSERT_TRUE(Result<std::string>("success").has_value());
-
- EXPECT_EQ("success", *Result<std::string>("success"));
- EXPECT_EQ("success", Result<std::string>("success").value());
-
- EXPECT_EQ('s', Result<std::string>("success")->data()[0]);
-}
-
-TEST(result, result_void) {
- Result<void> ok = {};
- EXPECT_RESULT_OK(ok);
- ok.value(); // should not crash
- ASSERT_DEATH(ok.error(), "");
-
- Result<void> fail = Error() << "failure" << 1;
- EXPECT_FALSE(fail.ok());
- EXPECT_EQ("failure1", fail.error().message());
- EXPECT_EQ(0, fail.error().code());
- EXPECT_TRUE(ok != fail);
- ASSERT_DEATH(fail.value(), "");
-
- auto test = [](bool ok) -> Result<void> {
- if (ok) return {};
- else return Error() << "failure" << 1;
- };
- EXPECT_TRUE(test(true).ok());
- EXPECT_FALSE(test(false).ok());
- test(true).value(); // should not crash
- ASSERT_DEATH(test(true).error(), "");
- ASSERT_DEATH(test(false).value(), "");
- EXPECT_EQ("failure1", test(false).error().message());
-}
-
-TEST(result, result_error) {
- Result<void> result = Error() << "failure" << 1;
- ASSERT_FALSE(result.ok());
- ASSERT_FALSE(result.has_value());
-
- EXPECT_EQ(0, result.error().code());
- EXPECT_EQ("failure1", result.error().message());
-}
-
-TEST(result, result_error_empty) {
- Result<void> result = Error();
- ASSERT_FALSE(result.ok());
- ASSERT_FALSE(result.has_value());
-
- EXPECT_EQ(0, result.error().code());
- EXPECT_EQ("", result.error().message());
-}
-
-TEST(result, result_error_rvalue) {
- // Error() and ErrnoError() aren't actually used to create a Result<T> object.
- // Under the hood, they are an intermediate class that can be implicitly constructed into a
- // Result<T>. This is needed both to create the ostream and because Error() itself, by
- // definition will not know what the type, T, of the underlying Result<T> object that it would
- // create is.
-
- auto MakeRvalueErrorResult = []() -> Result<void> { return Error() << "failure" << 1; };
- ASSERT_FALSE(MakeRvalueErrorResult().ok());
- ASSERT_FALSE(MakeRvalueErrorResult().has_value());
-
- EXPECT_EQ(0, MakeRvalueErrorResult().error().code());
- EXPECT_EQ("failure1", MakeRvalueErrorResult().error().message());
-}
-
-TEST(result, result_errno_error) {
- constexpr int test_errno = 6;
- errno = test_errno;
- Result<void> result = ErrnoError() << "failure" << 1;
-
- ASSERT_FALSE(result.ok());
- ASSERT_FALSE(result.has_value());
-
- EXPECT_EQ(test_errno, result.error().code());
- EXPECT_EQ("failure1: "s + strerror(test_errno), result.error().message());
-}
-
-TEST(result, result_errno_error_no_text) {
- constexpr int test_errno = 6;
- errno = test_errno;
- Result<void> result = ErrnoError();
-
- ASSERT_FALSE(result.ok());
- ASSERT_FALSE(result.has_value());
-
- EXPECT_EQ(test_errno, result.error().code());
- EXPECT_EQ(strerror(test_errno), result.error().message());
-}
-
-TEST(result, result_error_from_other_result) {
- auto error_text = "test error"s;
- Result<void> result = Error() << error_text;
-
- ASSERT_FALSE(result.ok());
- ASSERT_FALSE(result.has_value());
-
- Result<std::string> result2 = result.error();
-
- ASSERT_FALSE(result2.ok());
- ASSERT_FALSE(result2.has_value());
-
- EXPECT_EQ(0, result2.error().code());
- EXPECT_EQ(error_text, result2.error().message());
-}
-
-TEST(result, result_error_through_ostream) {
- auto error_text = "test error"s;
- Result<void> result = Error() << error_text;
-
- ASSERT_FALSE(result.ok());
- ASSERT_FALSE(result.has_value());
-
- Result<std::string> result2 = Error() << result.error();
-
- ASSERT_FALSE(result2.ok());
- ASSERT_FALSE(result2.has_value());
-
- EXPECT_EQ(0, result2.error().code());
- EXPECT_EQ(error_text, result2.error().message());
-}
-
-TEST(result, result_errno_error_through_ostream) {
- auto error_text = "test error"s;
- constexpr int test_errno = 6;
- errno = 6;
- Result<void> result = ErrnoError() << error_text;
-
- errno = 0;
-
- ASSERT_FALSE(result.ok());
- ASSERT_FALSE(result.has_value());
-
- Result<std::string> result2 = Error() << result.error();
-
- ASSERT_FALSE(result2.ok());
- ASSERT_FALSE(result2.has_value());
-
- EXPECT_EQ(test_errno, result2.error().code());
- EXPECT_EQ(error_text + ": " + strerror(test_errno), result2.error().message());
-}
-
-TEST(result, constructor_forwarding) {
- auto result = Result<std::string>(std::in_place, 5, 'a');
-
- ASSERT_RESULT_OK(result);
- ASSERT_TRUE(result.has_value());
-
- EXPECT_EQ("aaaaa", *result);
-}
-
-struct ConstructorTracker {
- static size_t constructor_called;
- static size_t copy_constructor_called;
- static size_t move_constructor_called;
- static size_t copy_assignment_called;
- static size_t move_assignment_called;
-
- template <typename T>
- ConstructorTracker(T&& string) : string(string) {
- ++constructor_called;
- }
-
- ConstructorTracker(const ConstructorTracker& ct) {
- ++copy_constructor_called;
- string = ct.string;
- }
- ConstructorTracker(ConstructorTracker&& ct) noexcept {
- ++move_constructor_called;
- string = std::move(ct.string);
- }
- ConstructorTracker& operator=(const ConstructorTracker& ct) {
- ++copy_assignment_called;
- string = ct.string;
- return *this;
- }
- ConstructorTracker& operator=(ConstructorTracker&& ct) noexcept {
- ++move_assignment_called;
- string = std::move(ct.string);
- return *this;
- }
-
- std::string string;
-};
-
-size_t ConstructorTracker::constructor_called = 0;
-size_t ConstructorTracker::copy_constructor_called = 0;
-size_t ConstructorTracker::move_constructor_called = 0;
-size_t ConstructorTracker::copy_assignment_called = 0;
-size_t ConstructorTracker::move_assignment_called = 0;
-
-Result<ConstructorTracker> ReturnConstructorTracker(const std::string& in) {
- if (in.empty()) {
- return "literal string";
- }
- if (in == "test2") {
- return ConstructorTracker(in + in + "2");
- }
- ConstructorTracker result(in + " " + in);
- return result;
-};
-
-TEST(result, no_copy_on_return) {
- // If returning parameters that may be used to implicitly construct the type T of Result<T>,
- // then those parameters are forwarded to the construction of Result<T>.
-
- // If returning an prvalue or xvalue, it will be move constructed during the construction of
- // Result<T>.
-
- // This check ensures that that is the case, and particularly that no copy constructors
- // are called.
-
- auto result1 = ReturnConstructorTracker("");
- ASSERT_RESULT_OK(result1);
- EXPECT_EQ("literal string", result1->string);
- EXPECT_EQ(1U, ConstructorTracker::constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
- EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
-
- auto result2 = ReturnConstructorTracker("test2");
- ASSERT_RESULT_OK(result2);
- EXPECT_EQ("test2test22", result2->string);
- EXPECT_EQ(2U, ConstructorTracker::constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
- EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
- EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
-
- auto result3 = ReturnConstructorTracker("test3");
- ASSERT_RESULT_OK(result3);
- EXPECT_EQ("test3 test3", result3->string);
- EXPECT_EQ(3U, ConstructorTracker::constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
- EXPECT_EQ(2U, ConstructorTracker::move_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
- EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
-}
-
-// Below two tests require that we do not hide the move constructor with our forwarding reference
-// constructor. This is done with by disabling the forwarding reference constructor if its first
-// and only type is Result<T>.
-TEST(result, result_result_with_success) {
- auto return_result_result_with_success = []() -> Result<Result<void>> { return Result<void>(); };
- auto result = return_result_result_with_success();
- ASSERT_RESULT_OK(result);
- ASSERT_RESULT_OK(*result);
-
- auto inner_result = result.value();
- ASSERT_RESULT_OK(inner_result);
-}
-
-TEST(result, result_result_with_failure) {
- auto return_result_result_with_error = []() -> Result<Result<void>> {
- return Result<void>(ResultError("failure string", 6));
- };
- auto result = return_result_result_with_error();
- ASSERT_RESULT_OK(result);
- ASSERT_FALSE(result->ok());
- EXPECT_EQ("failure string", (*result).error().message());
- EXPECT_EQ(6, (*result).error().code());
-}
-
-// This test requires that we disable the forwarding reference constructor if Result<T> is the
-// *only* type that we are forwarding. In otherwords, if we are forwarding Result<T>, int to
-// construct a Result<T>, then we still need the constructor.
-TEST(result, result_two_parameter_constructor_same_type) {
- struct TestStruct {
- TestStruct(int value) : value_(value) {}
- TestStruct(Result<TestStruct> result, int value) : value_(result->value_ * value) {}
- int value_;
- };
-
- auto return_test_struct = []() -> Result<TestStruct> {
- return Result<TestStruct>(std::in_place, Result<TestStruct>(std::in_place, 6), 6);
- };
-
- auto result = return_test_struct();
- ASSERT_RESULT_OK(result);
- EXPECT_EQ(36, result->value_);
-}
-
-TEST(result, die_on_access_failed_result) {
- Result<std::string> result = Error();
- ASSERT_DEATH(*result, "");
-}
-
-TEST(result, die_on_get_error_succesful_result) {
- Result<std::string> result = "success";
- ASSERT_DEATH(result.error(), "");
-}
-
-template <class CharT>
-std::basic_ostream<CharT>& SetErrnoToTwo(std::basic_ostream<CharT>& ss) {
- errno = 2;
- return ss;
-}
-
-TEST(result, preserve_errno) {
- errno = 1;
- int old_errno = errno;
- Result<int> result = Error() << "Failed" << SetErrnoToTwo<char>;
- ASSERT_FALSE(result.ok());
- EXPECT_EQ(old_errno, errno);
-
- errno = 1;
- old_errno = errno;
- Result<int> result2 = ErrnoError() << "Failed" << SetErrnoToTwo<char>;
- ASSERT_FALSE(result2.ok());
- EXPECT_EQ(old_errno, errno);
- EXPECT_EQ(old_errno, result2.error().code());
-}
-
-TEST(result, error_with_fmt) {
- Result<int> result = Errorf("{} {}!", "hello", "world");
- EXPECT_EQ("hello world!", result.error().message());
-
- result = Errorf("{} {}!", std::string("hello"), std::string("world"));
- EXPECT_EQ("hello world!", result.error().message());
-
- result = Errorf("{1} {0}!", "world", "hello");
- EXPECT_EQ("hello world!", result.error().message());
-
- result = Errorf("hello world!");
- EXPECT_EQ("hello world!", result.error().message());
-
- Result<int> result2 = Errorf("error occurred with {}", result.error());
- EXPECT_EQ("error occurred with hello world!", result2.error().message());
-
- constexpr int test_errno = 6;
- errno = test_errno;
- result = ErrnoErrorf("{} {}!", "hello", "world");
- EXPECT_EQ(test_errno, result.error().code());
- EXPECT_EQ("hello world!: "s + strerror(test_errno), result.error().message());
-}
-
-TEST(result, error_with_fmt_carries_errno) {
- constexpr int inner_errno = 6;
- errno = inner_errno;
- Result<int> inner_result = ErrnoErrorf("inner failure");
- errno = 0;
- EXPECT_EQ(inner_errno, inner_result.error().code());
-
- // outer_result is created with Errorf, but its error code is got from inner_result.
- Result<int> outer_result = Errorf("outer failure caused by {}", inner_result.error());
- EXPECT_EQ(inner_errno, outer_result.error().code());
- EXPECT_EQ("outer failure caused by inner failure: "s + strerror(inner_errno),
- outer_result.error().message());
-
- // now both result objects are created with ErrnoErrorf. errno from the inner_result
- // is not passed to outer_result.
- constexpr int outer_errno = 10;
- errno = outer_errno;
- outer_result = ErrnoErrorf("outer failure caused by {}", inner_result.error());
- EXPECT_EQ(outer_errno, outer_result.error().code());
- EXPECT_EQ("outer failure caused by inner failure: "s + strerror(inner_errno) + ": "s +
- strerror(outer_errno),
- outer_result.error().message());
-}
-
-TEST(result, errno_chaining_multiple) {
- constexpr int errno1 = 6;
- errno = errno1;
- Result<int> inner1 = ErrnoErrorf("error1");
-
- constexpr int errno2 = 10;
- errno = errno2;
- Result<int> inner2 = ErrnoErrorf("error2");
-
- // takes the error code of inner2 since its the last one.
- Result<int> outer = Errorf("two errors: {}, {}", inner1.error(), inner2.error());
- EXPECT_EQ(errno2, outer.error().code());
- EXPECT_EQ("two errors: error1: "s + strerror(errno1) + ", error2: "s + strerror(errno2),
- outer.error().message());
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/scopeguard_test.cpp b/base/scopeguard_test.cpp
deleted file mode 100644
index 9236d7b..0000000
--- a/base/scopeguard_test.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 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.
- */
-
-#include "android-base/scopeguard.h"
-
-#include <utility>
-#include <vector>
-
-#include <gtest/gtest.h>
-
-TEST(scopeguard, normal) {
- bool guarded_var = true;
- {
- auto scopeguard = android::base::make_scope_guard([&guarded_var] { guarded_var = false; });
- }
- ASSERT_FALSE(guarded_var);
-}
-
-TEST(scopeguard, disabled) {
- bool guarded_var = true;
- {
- auto scopeguard = android::base::make_scope_guard([&guarded_var] { guarded_var = false; });
- scopeguard.Disable();
- }
- ASSERT_TRUE(guarded_var);
-}
-
-TEST(scopeguard, moved) {
- int guarded_var = true;
- auto scopeguard = android::base::make_scope_guard([&guarded_var] { guarded_var = false; });
- { decltype(scopeguard) new_guard(std::move(scopeguard)); }
- EXPECT_FALSE(scopeguard.active());
- ASSERT_FALSE(guarded_var);
-}
-
-TEST(scopeguard, vector) {
- int guarded_var = 0;
- {
- std::vector<android::base::ScopeGuard<std::function<void()>>> scopeguards;
- scopeguards.emplace_back(android::base::make_scope_guard(
- std::bind([](int& guarded_var) { guarded_var++; }, std::ref(guarded_var))));
- scopeguards.emplace_back(android::base::make_scope_guard(
- std::bind([](int& guarded_var) { guarded_var++; }, std::ref(guarded_var))));
- }
- ASSERT_EQ(guarded_var, 2);
-}
diff --git a/base/stringprintf.cpp b/base/stringprintf.cpp
deleted file mode 100644
index 78e1e8d..0000000
--- a/base/stringprintf.cpp
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2011 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 "android-base/stringprintf.h"
-
-#include <stdio.h>
-
-#include <string>
-
-namespace android {
-namespace base {
-
-void StringAppendV(std::string* dst, const char* format, va_list ap) {
- // First try with a small fixed size buffer
- char space[1024];
-
- // It's possible for methods that use a va_list to invalidate
- // the data in it upon use. The fix is to make a copy
- // of the structure before using it and use that copy instead.
- va_list backup_ap;
- va_copy(backup_ap, ap);
- int result = vsnprintf(space, sizeof(space), format, backup_ap);
- va_end(backup_ap);
-
- if (result < static_cast<int>(sizeof(space))) {
- if (result >= 0) {
- // Normal case -- everything fit.
- dst->append(space, result);
- return;
- }
-
- if (result < 0) {
- // Just an error.
- return;
- }
- }
-
- // Increase the buffer size to the size requested by vsnprintf,
- // plus one for the closing \0.
- int length = result + 1;
- char* buf = new char[length];
-
- // Restore the va_list before we use it again
- va_copy(backup_ap, ap);
- result = vsnprintf(buf, length, format, backup_ap);
- va_end(backup_ap);
-
- if (result >= 0 && result < length) {
- // It fit
- dst->append(buf, result);
- }
- delete[] buf;
-}
-
-std::string StringPrintf(const char* fmt, ...) {
- va_list ap;
- va_start(ap, fmt);
- std::string result;
- StringAppendV(&result, fmt, ap);
- va_end(ap);
- return result;
-}
-
-void StringAppendF(std::string* dst, const char* format, ...) {
- va_list ap;
- va_start(ap, format);
- StringAppendV(dst, format, ap);
- va_end(ap);
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/stringprintf_test.cpp b/base/stringprintf_test.cpp
deleted file mode 100644
index fc009b1..0000000
--- a/base/stringprintf_test.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2011 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 "android-base/stringprintf.h"
-
-#include <gtest/gtest.h>
-
-#include <string>
-
-TEST(StringPrintfTest, HexSizeT) {
- size_t size = 0x00107e59;
- EXPECT_EQ("00107e59", android::base::StringPrintf("%08zx", size));
- EXPECT_EQ("0x00107e59", android::base::StringPrintf("0x%08zx", size));
-}
-
-TEST(StringPrintfTest, StringAppendF) {
- std::string s("a");
- android::base::StringAppendF(&s, "b");
- EXPECT_EQ("ab", s);
-}
-
-TEST(StringPrintfTest, Errno) {
- errno = 123;
- android::base::StringPrintf("hello %s", "world");
- EXPECT_EQ(123, errno);
-}
-
-void TestN(size_t n) {
- char* buf = new char[n + 1];
- memset(buf, 'x', n);
- buf[n] = '\0';
- std::string s(android::base::StringPrintf("%s", buf));
- EXPECT_EQ(buf, s);
- delete[] buf;
-}
-
-TEST(StringPrintfTest, At1023) {
- TestN(1023);
-}
-
-TEST(StringPrintfTest, At1024) {
- TestN(1024);
-}
-
-TEST(StringPrintfTest, At1025) {
- TestN(1025);
-}
diff --git a/base/strings.cpp b/base/strings.cpp
deleted file mode 100644
index 40b2bf2..0000000
--- a/base/strings.cpp
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/strings.h"
-
-#include <stdlib.h>
-#include <string.h>
-
-#include <string>
-#include <vector>
-
-namespace android {
-namespace base {
-
-#define CHECK_NE(a, b) \
- if ((a) == (b)) abort();
-
-std::vector<std::string> Split(const std::string& s,
- const std::string& delimiters) {
- CHECK_NE(delimiters.size(), 0U);
-
- std::vector<std::string> result;
-
- size_t base = 0;
- size_t found;
- while (true) {
- found = s.find_first_of(delimiters, base);
- result.push_back(s.substr(base, found - base));
- if (found == s.npos) break;
- base = found + 1;
- }
-
- return result;
-}
-
-std::string Trim(const std::string& s) {
- std::string result;
-
- if (s.size() == 0) {
- return result;
- }
-
- size_t start_index = 0;
- size_t end_index = s.size() - 1;
-
- // Skip initial whitespace.
- while (start_index < s.size()) {
- if (!isspace(s[start_index])) {
- break;
- }
- start_index++;
- }
-
- // Skip terminating whitespace.
- while (end_index >= start_index) {
- if (!isspace(s[end_index])) {
- break;
- }
- end_index--;
- }
-
- // All spaces, no beef.
- if (end_index < start_index) {
- return "";
- }
- // Start_index is the first non-space, end_index is the last one.
- return s.substr(start_index, end_index - start_index + 1);
-}
-
-// These cases are probably the norm, so we mark them extern in the header to
-// aid compile time and binary size.
-template std::string Join(const std::vector<std::string>&, char);
-template std::string Join(const std::vector<const char*>&, char);
-template std::string Join(const std::vector<std::string>&, const std::string&);
-template std::string Join(const std::vector<const char*>&, const std::string&);
-
-bool StartsWith(std::string_view s, std::string_view prefix) {
- return s.substr(0, prefix.size()) == prefix;
-}
-
-bool StartsWith(std::string_view s, char prefix) {
- return !s.empty() && s.front() == prefix;
-}
-
-bool StartsWithIgnoreCase(std::string_view s, std::string_view prefix) {
- return s.size() >= prefix.size() && strncasecmp(s.data(), prefix.data(), prefix.size()) == 0;
-}
-
-bool EndsWith(std::string_view s, std::string_view suffix) {
- return s.size() >= suffix.size() && s.substr(s.size() - suffix.size(), suffix.size()) == suffix;
-}
-
-bool EndsWith(std::string_view s, char suffix) {
- return !s.empty() && s.back() == suffix;
-}
-
-bool EndsWithIgnoreCase(std::string_view s, std::string_view suffix) {
- return s.size() >= suffix.size() &&
- strncasecmp(s.data() + (s.size() - suffix.size()), suffix.data(), suffix.size()) == 0;
-}
-
-bool EqualsIgnoreCase(std::string_view lhs, std::string_view rhs) {
- return lhs.size() == rhs.size() && strncasecmp(lhs.data(), rhs.data(), lhs.size()) == 0;
-}
-
-std::string StringReplace(std::string_view s, std::string_view from, std::string_view to,
- bool all) {
- if (from.empty()) return std::string(s);
-
- std::string result;
- std::string_view::size_type start_pos = 0;
- do {
- std::string_view::size_type pos = s.find(from, start_pos);
- if (pos == std::string_view::npos) break;
-
- result.append(s.data() + start_pos, pos - start_pos);
- result.append(to.data(), to.size());
-
- start_pos = pos + from.size();
- } while (all);
- result.append(s.data() + start_pos, s.size() - start_pos);
- return result;
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/strings_test.cpp b/base/strings_test.cpp
deleted file mode 100644
index 5ae3094..0000000
--- a/base/strings_test.cpp
+++ /dev/null
@@ -1,356 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/strings.h"
-
-#include <gtest/gtest.h>
-
-#include <string>
-#include <vector>
-#include <set>
-#include <unordered_set>
-
-TEST(strings, split_empty) {
- std::vector<std::string> parts = android::base::Split("", ",");
- ASSERT_EQ(1U, parts.size());
- ASSERT_EQ("", parts[0]);
-}
-
-TEST(strings, split_single) {
- std::vector<std::string> parts = android::base::Split("foo", ",");
- ASSERT_EQ(1U, parts.size());
- ASSERT_EQ("foo", parts[0]);
-}
-
-TEST(strings, split_simple) {
- std::vector<std::string> parts = android::base::Split("foo,bar,baz", ",");
- ASSERT_EQ(3U, parts.size());
- ASSERT_EQ("foo", parts[0]);
- ASSERT_EQ("bar", parts[1]);
- ASSERT_EQ("baz", parts[2]);
-}
-
-TEST(strings, split_with_empty_part) {
- std::vector<std::string> parts = android::base::Split("foo,,bar", ",");
- ASSERT_EQ(3U, parts.size());
- ASSERT_EQ("foo", parts[0]);
- ASSERT_EQ("", parts[1]);
- ASSERT_EQ("bar", parts[2]);
-}
-
-TEST(strings, split_with_trailing_empty_part) {
- std::vector<std::string> parts = android::base::Split("foo,bar,", ",");
- ASSERT_EQ(3U, parts.size());
- ASSERT_EQ("foo", parts[0]);
- ASSERT_EQ("bar", parts[1]);
- ASSERT_EQ("", parts[2]);
-}
-
-TEST(strings, split_null_char) {
- std::vector<std::string> parts =
- android::base::Split(std::string("foo\0bar", 7), std::string("\0", 1));
- ASSERT_EQ(2U, parts.size());
- ASSERT_EQ("foo", parts[0]);
- ASSERT_EQ("bar", parts[1]);
-}
-
-TEST(strings, split_any) {
- std::vector<std::string> parts = android::base::Split("foo:bar,baz", ",:");
- ASSERT_EQ(3U, parts.size());
- ASSERT_EQ("foo", parts[0]);
- ASSERT_EQ("bar", parts[1]);
- ASSERT_EQ("baz", parts[2]);
-}
-
-TEST(strings, split_any_with_empty_part) {
- std::vector<std::string> parts = android::base::Split("foo:,bar", ",:");
- ASSERT_EQ(3U, parts.size());
- ASSERT_EQ("foo", parts[0]);
- ASSERT_EQ("", parts[1]);
- ASSERT_EQ("bar", parts[2]);
-}
-
-TEST(strings, trim_empty) {
- ASSERT_EQ("", android::base::Trim(""));
-}
-
-TEST(strings, trim_already_trimmed) {
- ASSERT_EQ("foo", android::base::Trim("foo"));
-}
-
-TEST(strings, trim_left) {
- ASSERT_EQ("foo", android::base::Trim(" foo"));
-}
-
-TEST(strings, trim_right) {
- ASSERT_EQ("foo", android::base::Trim("foo "));
-}
-
-TEST(strings, trim_both) {
- ASSERT_EQ("foo", android::base::Trim(" foo "));
-}
-
-TEST(strings, trim_no_trim_middle) {
- ASSERT_EQ("foo bar", android::base::Trim("foo bar"));
-}
-
-TEST(strings, trim_other_whitespace) {
- ASSERT_EQ("foo", android::base::Trim("\v\tfoo\n\f"));
-}
-
-TEST(strings, join_nothing) {
- std::vector<std::string> list = {};
- ASSERT_EQ("", android::base::Join(list, ','));
-}
-
-TEST(strings, join_single) {
- std::vector<std::string> list = {"foo"};
- ASSERT_EQ("foo", android::base::Join(list, ','));
-}
-
-TEST(strings, join_simple) {
- std::vector<std::string> list = {"foo", "bar", "baz"};
- ASSERT_EQ("foo,bar,baz", android::base::Join(list, ','));
-}
-
-TEST(strings, join_separator_in_vector) {
- std::vector<std::string> list = {",", ","};
- ASSERT_EQ(",,,", android::base::Join(list, ','));
-}
-
-TEST(strings, join_simple_ints) {
- std::set<int> list = {1, 2, 3};
- ASSERT_EQ("1,2,3", android::base::Join(list, ','));
-}
-
-TEST(strings, join_unordered_set) {
- std::unordered_set<int> list = {1, 2};
- ASSERT_TRUE("1,2" == android::base::Join(list, ',') ||
- "2,1" == android::base::Join(list, ','));
-}
-
-TEST(strings, StartsWith_empty) {
- ASSERT_FALSE(android::base::StartsWith("", "foo"));
- ASSERT_TRUE(android::base::StartsWith("", ""));
-}
-
-TEST(strings, StartsWithIgnoreCase_empty) {
- ASSERT_FALSE(android::base::StartsWithIgnoreCase("", "foo"));
- ASSERT_TRUE(android::base::StartsWithIgnoreCase("", ""));
-}
-
-TEST(strings, StartsWith_simple) {
- ASSERT_TRUE(android::base::StartsWith("foo", ""));
- ASSERT_TRUE(android::base::StartsWith("foo", "f"));
- ASSERT_TRUE(android::base::StartsWith("foo", "fo"));
- ASSERT_TRUE(android::base::StartsWith("foo", "foo"));
-}
-
-TEST(strings, StartsWithIgnoreCase_simple) {
- ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", ""));
- ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "f"));
- ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "F"));
- ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "fo"));
- ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "fO"));
- ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "Fo"));
- ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "FO"));
- ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "foo"));
- ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "foO"));
- ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "fOo"));
- ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "fOO"));
- ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "Foo"));
- ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "FoO"));
- ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "FOo"));
- ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "FOO"));
-}
-
-TEST(strings, StartsWith_prefix_too_long) {
- ASSERT_FALSE(android::base::StartsWith("foo", "foobar"));
-}
-
-TEST(strings, StartsWithIgnoreCase_prefix_too_long) {
- ASSERT_FALSE(android::base::StartsWithIgnoreCase("foo", "foobar"));
- ASSERT_FALSE(android::base::StartsWithIgnoreCase("foo", "FOOBAR"));
-}
-
-TEST(strings, StartsWith_contains_prefix) {
- ASSERT_FALSE(android::base::StartsWith("foobar", "oba"));
- ASSERT_FALSE(android::base::StartsWith("foobar", "bar"));
-}
-
-TEST(strings, StartsWithIgnoreCase_contains_prefix) {
- ASSERT_FALSE(android::base::StartsWithIgnoreCase("foobar", "oba"));
- ASSERT_FALSE(android::base::StartsWithIgnoreCase("foobar", "OBA"));
- ASSERT_FALSE(android::base::StartsWithIgnoreCase("foobar", "bar"));
- ASSERT_FALSE(android::base::StartsWithIgnoreCase("foobar", "BAR"));
-}
-
-TEST(strings, StartsWith_char) {
- ASSERT_FALSE(android::base::StartsWith("", 'f'));
- ASSERT_TRUE(android::base::StartsWith("foo", 'f'));
- ASSERT_FALSE(android::base::StartsWith("foo", 'o'));
-}
-
-TEST(strings, EndsWith_empty) {
- ASSERT_FALSE(android::base::EndsWith("", "foo"));
- ASSERT_TRUE(android::base::EndsWith("", ""));
-}
-
-TEST(strings, EndsWithIgnoreCase_empty) {
- ASSERT_FALSE(android::base::EndsWithIgnoreCase("", "foo"));
- ASSERT_FALSE(android::base::EndsWithIgnoreCase("", "FOO"));
- ASSERT_TRUE(android::base::EndsWithIgnoreCase("", ""));
-}
-
-TEST(strings, EndsWith_simple) {
- ASSERT_TRUE(android::base::EndsWith("foo", ""));
- ASSERT_TRUE(android::base::EndsWith("foo", "o"));
- ASSERT_TRUE(android::base::EndsWith("foo", "oo"));
- ASSERT_TRUE(android::base::EndsWith("foo", "foo"));
-}
-
-TEST(strings, EndsWithIgnoreCase_simple) {
- ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", ""));
- ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "o"));
- ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "O"));
- ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "oo"));
- ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "oO"));
- ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "Oo"));
- ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "OO"));
- ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "foo"));
- ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "foO"));
- ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "fOo"));
- ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "fOO"));
- ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "Foo"));
- ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "FoO"));
- ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "FOo"));
- ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "FOO"));
-}
-
-TEST(strings, EndsWith_prefix_too_long) {
- ASSERT_FALSE(android::base::EndsWith("foo", "foobar"));
-}
-
-TEST(strings, EndsWithIgnoreCase_prefix_too_long) {
- ASSERT_FALSE(android::base::EndsWithIgnoreCase("foo", "foobar"));
- ASSERT_FALSE(android::base::EndsWithIgnoreCase("foo", "FOOBAR"));
-}
-
-TEST(strings, EndsWith_contains_prefix) {
- ASSERT_FALSE(android::base::EndsWith("foobar", "oba"));
- ASSERT_FALSE(android::base::EndsWith("foobar", "foo"));
-}
-
-TEST(strings, EndsWithIgnoreCase_contains_prefix) {
- ASSERT_FALSE(android::base::EndsWithIgnoreCase("foobar", "OBA"));
- ASSERT_FALSE(android::base::EndsWithIgnoreCase("foobar", "FOO"));
-}
-
-TEST(strings, StartsWith_std_string) {
- ASSERT_TRUE(android::base::StartsWith("hello", std::string{"hell"}));
- ASSERT_FALSE(android::base::StartsWith("goodbye", std::string{"hell"}));
-}
-
-TEST(strings, StartsWithIgnoreCase_std_string) {
- ASSERT_TRUE(android::base::StartsWithIgnoreCase("HeLlO", std::string{"hell"}));
- ASSERT_FALSE(android::base::StartsWithIgnoreCase("GoOdByE", std::string{"hell"}));
-}
-
-TEST(strings, EndsWith_std_string) {
- ASSERT_TRUE(android::base::EndsWith("hello", std::string{"lo"}));
- ASSERT_FALSE(android::base::EndsWith("goodbye", std::string{"lo"}));
-}
-
-TEST(strings, EndsWithIgnoreCase_std_string) {
- ASSERT_TRUE(android::base::EndsWithIgnoreCase("HeLlO", std::string{"lo"}));
- ASSERT_FALSE(android::base::EndsWithIgnoreCase("GoOdByE", std::string{"lo"}));
-}
-
-TEST(strings, EndsWith_char) {
- ASSERT_FALSE(android::base::EndsWith("", 'o'));
- ASSERT_TRUE(android::base::EndsWith("foo", 'o'));
- ASSERT_FALSE(android::base::EndsWith("foo", "f"));
-}
-
-TEST(strings, EqualsIgnoreCase) {
- ASSERT_TRUE(android::base::EqualsIgnoreCase("foo", "FOO"));
- ASSERT_TRUE(android::base::EqualsIgnoreCase("FOO", "foo"));
- ASSERT_FALSE(android::base::EqualsIgnoreCase("foo", "bar"));
- ASSERT_FALSE(android::base::EqualsIgnoreCase("foo", "fool"));
-}
-
-TEST(strings, ubsan_28729303) {
- android::base::Split("/dev/null", ":");
-}
-
-TEST(strings, ConsumePrefix) {
- std::string_view s{"foo.bar"};
- ASSERT_FALSE(android::base::ConsumePrefix(&s, "bar."));
- ASSERT_EQ("foo.bar", s);
- ASSERT_TRUE(android::base::ConsumePrefix(&s, "foo."));
- ASSERT_EQ("bar", s);
-}
-
-TEST(strings, ConsumeSuffix) {
- std::string_view s{"foo.bar"};
- ASSERT_FALSE(android::base::ConsumeSuffix(&s, ".foo"));
- ASSERT_EQ("foo.bar", s);
- ASSERT_TRUE(android::base::ConsumeSuffix(&s, ".bar"));
- ASSERT_EQ("foo", s);
-}
-
-TEST(strings, StringReplace_false) {
- // No change.
- ASSERT_EQ("abcabc", android::base::StringReplace("abcabc", "z", "Z", false));
- ASSERT_EQ("", android::base::StringReplace("", "z", "Z", false));
- ASSERT_EQ("abcabc", android::base::StringReplace("abcabc", "", "Z", false));
-
- // Equal lengths.
- ASSERT_EQ("Abcabc", android::base::StringReplace("abcabc", "a", "A", false));
- ASSERT_EQ("aBcabc", android::base::StringReplace("abcabc", "b", "B", false));
- ASSERT_EQ("abCabc", android::base::StringReplace("abcabc", "c", "C", false));
-
- // Longer replacement.
- ASSERT_EQ("foobcabc", android::base::StringReplace("abcabc", "a", "foo", false));
- ASSERT_EQ("afoocabc", android::base::StringReplace("abcabc", "b", "foo", false));
- ASSERT_EQ("abfooabc", android::base::StringReplace("abcabc", "c", "foo", false));
-
- // Shorter replacement.
- ASSERT_EQ("xxyz", android::base::StringReplace("abcxyz", "abc", "x", false));
- ASSERT_EQ("axyz", android::base::StringReplace("abcxyz", "bcx", "x", false));
- ASSERT_EQ("abcx", android::base::StringReplace("abcxyz", "xyz", "x", false));
-}
-
-TEST(strings, StringReplace_true) {
- // No change.
- ASSERT_EQ("abcabc", android::base::StringReplace("abcabc", "z", "Z", true));
- ASSERT_EQ("", android::base::StringReplace("", "z", "Z", true));
- ASSERT_EQ("abcabc", android::base::StringReplace("abcabc", "", "Z", true));
-
- // Equal lengths.
- ASSERT_EQ("AbcAbc", android::base::StringReplace("abcabc", "a", "A", true));
- ASSERT_EQ("aBcaBc", android::base::StringReplace("abcabc", "b", "B", true));
- ASSERT_EQ("abCabC", android::base::StringReplace("abcabc", "c", "C", true));
-
- // Longer replacement.
- ASSERT_EQ("foobcfoobc", android::base::StringReplace("abcabc", "a", "foo", true));
- ASSERT_EQ("afoocafooc", android::base::StringReplace("abcabc", "b", "foo", true));
- ASSERT_EQ("abfooabfoo", android::base::StringReplace("abcabc", "c", "foo", true));
-
- // Shorter replacement.
- ASSERT_EQ("xxyzx", android::base::StringReplace("abcxyzabc", "abc", "x", true));
- ASSERT_EQ("<xx>", android::base::StringReplace("<abcabc>", "abc", "x", true));
-}
diff --git a/base/test_main.cpp b/base/test_main.cpp
deleted file mode 100644
index 7fa6a84..0000000
--- a/base/test_main.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-
-#include "android-base/logging.h"
-
-int main(int argc, char** argv) {
- ::testing::InitGoogleTest(&argc, argv);
- android::base::InitLogging(argv, android::base::StderrLogger);
- return RUN_ALL_TESTS();
-}
diff --git a/base/test_utils.cpp b/base/test_utils.cpp
deleted file mode 100644
index 36b4cdf..0000000
--- a/base/test_utils.cpp
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/test_utils.h"
-
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include <string>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-
-CapturedStdFd::CapturedStdFd(int std_fd) : std_fd_(std_fd), old_fd_(-1) {
- Start();
-}
-
-CapturedStdFd::~CapturedStdFd() {
- if (old_fd_ != -1) {
- Stop();
- }
-}
-
-int CapturedStdFd::fd() const {
- return temp_file_.fd;
-}
-
-std::string CapturedStdFd::str() {
- std::string result;
- CHECK_EQ(0, TEMP_FAILURE_RETRY(lseek(fd(), 0, SEEK_SET)));
- android::base::ReadFdToString(fd(), &result);
- return result;
-}
-
-void CapturedStdFd::Reset() {
- // Do not reset while capturing.
- CHECK_EQ(-1, old_fd_);
- CHECK_EQ(0, TEMP_FAILURE_RETRY(lseek(fd(), 0, SEEK_SET)));
- CHECK_EQ(0, ftruncate(fd(), 0));
-}
-
-void CapturedStdFd::Start() {
-#if defined(_WIN32)
- // On Windows, stderr is often buffered, so make sure it is unbuffered so
- // that we can immediately read back what was written to stderr.
- if (std_fd_ == STDERR_FILENO) CHECK_EQ(0, setvbuf(stderr, nullptr, _IONBF, 0));
-#endif
- old_fd_ = dup(std_fd_);
- CHECK_NE(-1, old_fd_);
- CHECK_NE(-1, dup2(fd(), std_fd_));
-}
-
-void CapturedStdFd::Stop() {
- CHECK_NE(-1, old_fd_);
- CHECK_NE(-1, dup2(old_fd_, std_fd_));
- close(old_fd_);
- old_fd_ = -1;
- // Note: cannot restore prior setvbuf() setting.
-}
diff --git a/base/test_utils_test.cpp b/base/test_utils_test.cpp
deleted file mode 100644
index 15a79dd..0000000
--- a/base/test_utils_test.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * 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.
- */
-
-#include <stdio.h>
-
-#include "android-base/test_utils.h"
-
-#include <gtest/gtest-spi.h>
-#include <gtest/gtest.h>
-
-namespace android {
-namespace base {
-
-TEST(TestUtilsTest, AssertMatch) {
- ASSERT_MATCH("foobar", R"(fo+baz?r)");
- EXPECT_FATAL_FAILURE(ASSERT_MATCH("foobar", R"(foobaz)"), "regex mismatch");
-}
-
-TEST(TestUtilsTest, AssertNotMatch) {
- ASSERT_NOT_MATCH("foobar", R"(foobaz)");
- EXPECT_FATAL_FAILURE(ASSERT_NOT_MATCH("foobar", R"(foobar)"), "regex mismatch");
-}
-
-TEST(TestUtilsTest, ExpectMatch) {
- EXPECT_MATCH("foobar", R"(fo+baz?r)");
- EXPECT_NONFATAL_FAILURE(EXPECT_MATCH("foobar", R"(foobaz)"), "regex mismatch");
-}
-
-TEST(TestUtilsTest, ExpectNotMatch) {
- EXPECT_NOT_MATCH("foobar", R"(foobaz)");
- EXPECT_NONFATAL_FAILURE(EXPECT_NOT_MATCH("foobar", R"(foobar)"), "regex mismatch");
-}
-
-TEST(TestUtilsTest, CaptureStdout_smoke) {
- CapturedStdout cap;
- printf("This should be captured.\n");
- cap.Stop();
- printf("This will not be captured.\n");
- ASSERT_EQ("This should be captured.\n", cap.str());
-
- cap.Start();
- printf("And this text should be captured too.\n");
- cap.Stop();
- ASSERT_EQ("This should be captured.\nAnd this text should be captured too.\n", cap.str());
-
- printf("Still not going to be captured.\n");
- cap.Reset();
- cap.Start();
- printf("Only this will be captured.\n");
- ASSERT_EQ("Only this will be captured.\n", cap.str());
-}
-
-TEST(TestUtilsTest, CaptureStderr_smoke) {
- CapturedStderr cap;
- fprintf(stderr, "This should be captured.\n");
- cap.Stop();
- fprintf(stderr, "This will not be captured.\n");
- ASSERT_EQ("This should be captured.\n", cap.str());
-
- cap.Start();
- fprintf(stderr, "And this text should be captured too.\n");
- cap.Stop();
- ASSERT_EQ("This should be captured.\nAnd this text should be captured too.\n", cap.str());
-
- fprintf(stderr, "Still not going to be captured.\n");
- cap.Reset();
- cap.Start();
- fprintf(stderr, "Only this will be captured.\n");
- ASSERT_EQ("Only this will be captured.\n", cap.str());
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/threads.cpp b/base/threads.cpp
deleted file mode 100644
index 48f6197..0000000
--- a/base/threads.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <android-base/threads.h>
-
-#include <stdint.h>
-#include <unistd.h>
-
-#if defined(__APPLE__)
-#include <pthread.h>
-#elif defined(__linux__) && !defined(__ANDROID__)
-#include <syscall.h>
-#elif defined(_WIN32)
-#include <windows.h>
-#endif
-
-namespace android {
-namespace base {
-
-uint64_t GetThreadId() {
-#if defined(__BIONIC__)
- return gettid();
-#elif defined(__APPLE__)
- uint64_t tid;
- pthread_threadid_np(NULL, &tid);
- return tid;
-#elif defined(__linux__)
- return syscall(__NR_gettid);
-#elif defined(_WIN32)
- return GetCurrentThreadId();
-#endif
-}
-
-} // namespace base
-} // namespace android
-
-#if defined(__GLIBC__)
-int tgkill(int tgid, int tid, int sig) {
- return syscall(__NR_tgkill, tgid, tid, sig);
-}
-#endif
diff --git a/base/utf8.cpp b/base/utf8.cpp
deleted file mode 100644
index adb46d0..0000000
--- a/base/utf8.cpp
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <windows.h>
-
-#include "android-base/utf8.h"
-
-#include <fcntl.h>
-#include <stdio.h>
-
-#include <algorithm>
-#include <string>
-
-#include "android-base/logging.h"
-
-namespace android {
-namespace base {
-
-// Helper to set errno based on GetLastError() after WideCharToMultiByte()/MultiByteToWideChar().
-static void SetErrnoFromLastError() {
- switch (GetLastError()) {
- case ERROR_NO_UNICODE_TRANSLATION:
- errno = EILSEQ;
- break;
- default:
- errno = EINVAL;
- break;
- }
-}
-
-bool WideToUTF8(const wchar_t* utf16, const size_t size, std::string* utf8) {
- utf8->clear();
-
- if (size == 0) {
- return true;
- }
-
- // TODO: Consider using std::wstring_convert once libcxx is supported on
- // Windows.
-
- // Only Vista or later has this flag that causes WideCharToMultiByte() to
- // return an error on invalid characters.
- const DWORD flags =
-#if (WINVER >= 0x0600)
- WC_ERR_INVALID_CHARS;
-#else
- 0;
-#endif
-
- const int chars_required = WideCharToMultiByte(CP_UTF8, flags, utf16, size,
- NULL, 0, NULL, NULL);
- if (chars_required <= 0) {
- SetErrnoFromLastError();
- return false;
- }
-
- // This could potentially throw a std::bad_alloc exception.
- utf8->resize(chars_required);
-
- const int result = WideCharToMultiByte(CP_UTF8, flags, utf16, size,
- &(*utf8)[0], chars_required, NULL,
- NULL);
- if (result != chars_required) {
- SetErrnoFromLastError();
- CHECK_LE(result, chars_required) << "WideCharToMultiByte wrote " << result
- << " chars to buffer of " << chars_required << " chars";
- utf8->clear();
- return false;
- }
-
- return true;
-}
-
-bool WideToUTF8(const wchar_t* utf16, std::string* utf8) {
- // Compute string length of NULL-terminated string with wcslen().
- return WideToUTF8(utf16, wcslen(utf16), utf8);
-}
-
-bool WideToUTF8(const std::wstring& utf16, std::string* utf8) {
- // Use the stored length of the string which allows embedded NULL characters
- // to be converted.
- return WideToUTF8(utf16.c_str(), utf16.length(), utf8);
-}
-
-// Internal helper function that takes MultiByteToWideChar() flags.
-static bool UTF8ToWideWithFlags(const char* utf8, const size_t size, std::wstring* utf16,
- const DWORD flags) {
- utf16->clear();
-
- if (size == 0) {
- return true;
- }
-
- // TODO: Consider using std::wstring_convert once libcxx is supported on
- // Windows.
- const int chars_required = MultiByteToWideChar(CP_UTF8, flags, utf8, size,
- NULL, 0);
- if (chars_required <= 0) {
- SetErrnoFromLastError();
- return false;
- }
-
- // This could potentially throw a std::bad_alloc exception.
- utf16->resize(chars_required);
-
- const int result = MultiByteToWideChar(CP_UTF8, flags, utf8, size,
- &(*utf16)[0], chars_required);
- if (result != chars_required) {
- SetErrnoFromLastError();
- CHECK_LE(result, chars_required) << "MultiByteToWideChar wrote " << result
- << " chars to buffer of " << chars_required << " chars";
- utf16->clear();
- return false;
- }
-
- return true;
-}
-
-bool UTF8ToWide(const char* utf8, const size_t size, std::wstring* utf16) {
- // If strictly interpreting as UTF-8 succeeds, return success.
- if (UTF8ToWideWithFlags(utf8, size, utf16, MB_ERR_INVALID_CHARS)) {
- return true;
- }
-
- const int saved_errno = errno;
-
- // Fallback to non-strict interpretation, allowing invalid characters and
- // converting as best as possible, and return false to signify a problem.
- (void)UTF8ToWideWithFlags(utf8, size, utf16, 0);
- errno = saved_errno;
- return false;
-}
-
-bool UTF8ToWide(const char* utf8, std::wstring* utf16) {
- // Compute string length of NULL-terminated string with strlen().
- return UTF8ToWide(utf8, strlen(utf8), utf16);
-}
-
-bool UTF8ToWide(const std::string& utf8, std::wstring* utf16) {
- // Use the stored length of the string which allows embedded NULL characters
- // to be converted.
- return UTF8ToWide(utf8.c_str(), utf8.length(), utf16);
-}
-
-static bool isDriveLetter(wchar_t c) {
- return (c >= L'a' && c <= L'z') || (c >= L'A' && c <= L'Z');
-}
-
-bool UTF8PathToWindowsLongPath(const char* utf8, std::wstring* utf16) {
- if (!UTF8ToWide(utf8, utf16)) {
- return false;
- }
- // Note: Although most Win32 File I/O API are limited to MAX_PATH (260
- // characters), the CreateDirectory API is limited to 248 characters.
- if (utf16->length() >= 248) {
- // If path is of the form "x:\" or "x:/"
- if (isDriveLetter((*utf16)[0]) && (*utf16)[1] == L':' &&
- ((*utf16)[2] == L'\\' || (*utf16)[2] == L'/')) {
- // Append long path prefix, and make sure there are no unix-style
- // separators to ensure a fully compliant Win32 long path string.
- utf16->insert(0, LR"(\\?\)");
- std::replace(utf16->begin(), utf16->end(), L'/', L'\\');
- }
- }
- return true;
-}
-
-// Versions of standard library APIs that support UTF-8 strings.
-namespace utf8 {
-
-FILE* fopen(const char* name, const char* mode) {
- std::wstring name_utf16;
- if (!UTF8PathToWindowsLongPath(name, &name_utf16)) {
- return nullptr;
- }
-
- std::wstring mode_utf16;
- if (!UTF8ToWide(mode, &mode_utf16)) {
- return nullptr;
- }
-
- return _wfopen(name_utf16.c_str(), mode_utf16.c_str());
-}
-
-int mkdir(const char* name, mode_t) {
- std::wstring name_utf16;
- if (!UTF8PathToWindowsLongPath(name, &name_utf16)) {
- return -1;
- }
-
- return _wmkdir(name_utf16.c_str());
-}
-
-int open(const char* name, int flags, ...) {
- std::wstring name_utf16;
- if (!UTF8PathToWindowsLongPath(name, &name_utf16)) {
- return -1;
- }
-
- int mode = 0;
- if ((flags & O_CREAT) != 0) {
- va_list args;
- va_start(args, flags);
- mode = va_arg(args, int);
- va_end(args);
- }
-
- return _wopen(name_utf16.c_str(), flags, mode);
-}
-
-int unlink(const char* name) {
- std::wstring name_utf16;
- if (!UTF8PathToWindowsLongPath(name, &name_utf16)) {
- return -1;
- }
-
- return _wunlink(name_utf16.c_str());
-}
-
-} // namespace utf8
-} // namespace base
-} // namespace android
diff --git a/base/utf8_test.cpp b/base/utf8_test.cpp
deleted file mode 100644
index 472e82c..0000000
--- a/base/utf8_test.cpp
+++ /dev/null
@@ -1,488 +0,0 @@
-/*
-* Copyright (C) 2015 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-
-#include "android-base/utf8.h"
-
-#include <gtest/gtest.h>
-
-#include <fcntl.h>
-#include <stdlib.h>
-
-#include "android-base/file.h"
-#include "android-base/macros.h"
-#include "android-base/unique_fd.h"
-
-namespace android {
-namespace base {
-
-TEST(UTFStringConversionsTest, ConvertInvalidUTF8) {
- std::wstring wide;
-
- errno = 0;
-
- // Standalone \xa2 is an invalid UTF-8 sequence, so this should return an
- // error. Concatenate two C/C++ literal string constants to prevent the
- // compiler from giving an error about "\xa2af" containing a "hex escape
- // sequence out of range".
- EXPECT_FALSE(android::base::UTF8ToWide("before\xa2" "after", &wide));
-
- EXPECT_EQ(EILSEQ, errno);
-
- // Even if an invalid character is encountered, UTF8ToWide() should still do
- // its best to convert the rest of the string. sysdeps_win32.cpp:
- // _console_write_utf8() depends on this behavior.
- //
- // Thus, we verify that the valid characters are converted, but we ignore the
- // specific replacement character that UTF8ToWide() may replace the invalid
- // UTF-8 characters with because we want to allow that to change if the
- // implementation changes.
- EXPECT_EQ(0U, wide.find(L"before"));
- const wchar_t after_wide[] = L"after";
- EXPECT_EQ(wide.length() - (arraysize(after_wide) - 1), wide.find(after_wide));
-}
-
-// Below is adapted from https://chromium.googlesource.com/chromium/src/+/master/base/strings/utf_string_conversions_unittest.cc
-
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// The tests below from utf_string_conversions_unittest.cc check for this
-// preprocessor symbol, so define it, as it is appropriate for Windows.
-#define WCHAR_T_IS_UTF16
-static_assert(sizeof(wchar_t) == 2, "wchar_t is not 2 bytes");
-
-// The tests below from utf_string_conversions_unittest.cc call versions of
-// UTF8ToWide() and WideToUTF8() that don't return success/failure, so these are
-// stub implementations with that signature. These are just for testing and
-// should not be moved to base because they assert/expect no errors which is
-// probably not a good idea (or at least it is something that should be left
-// up to the caller, not a base library).
-
-static std::wstring UTF8ToWide(const std::string& utf8) {
- std::wstring utf16;
- EXPECT_TRUE(UTF8ToWide(utf8, &utf16));
- return utf16;
-}
-
-static std::string WideToUTF8(const std::wstring& utf16) {
- std::string utf8;
- EXPECT_TRUE(WideToUTF8(utf16, &utf8));
- return utf8;
-}
-
-namespace {
-
-const wchar_t* const kConvertRoundtripCases[] = {
- L"Google Video",
- // "网页 图片 资讯更多 »"
- L"\x7f51\x9875\x0020\x56fe\x7247\x0020\x8d44\x8baf\x66f4\x591a\x0020\x00bb",
- // "Παγκόσμιος Ιστός"
- L"\x03a0\x03b1\x03b3\x03ba\x03cc\x03c3\x03bc\x03b9"
- L"\x03bf\x03c2\x0020\x0399\x03c3\x03c4\x03cc\x03c2",
- // "Поиск страниц на русском"
- L"\x041f\x043e\x0438\x0441\x043a\x0020\x0441\x0442"
- L"\x0440\x0430\x043d\x0438\x0446\x0020\x043d\x0430"
- L"\x0020\x0440\x0443\x0441\x0441\x043a\x043e\x043c",
- // "전체서비스"
- L"\xc804\xccb4\xc11c\xbe44\xc2a4",
-
- // Test characters that take more than 16 bits. This will depend on whether
- // wchar_t is 16 or 32 bits.
-#if defined(WCHAR_T_IS_UTF16)
- L"\xd800\xdf00",
- // ????? (Mathematical Alphanumeric Symbols (U+011d40 - U+011d44 : A,B,C,D,E)
- L"\xd807\xdd40\xd807\xdd41\xd807\xdd42\xd807\xdd43\xd807\xdd44",
-#elif defined(WCHAR_T_IS_UTF32)
- L"\x10300",
- // ????? (Mathematical Alphanumeric Symbols (U+011d40 - U+011d44 : A,B,C,D,E)
- L"\x11d40\x11d41\x11d42\x11d43\x11d44",
-#endif
-};
-
-} // namespace
-
-TEST(UTFStringConversionsTest, ConvertUTF8AndWide) {
- // we round-trip all the wide strings through UTF-8 to make sure everything
- // agrees on the conversion. This uses the stream operators to test them
- // simultaneously.
- for (size_t i = 0; i < arraysize(kConvertRoundtripCases); ++i) {
- std::ostringstream utf8;
- utf8 << WideToUTF8(kConvertRoundtripCases[i]);
- std::wostringstream wide;
- wide << UTF8ToWide(utf8.str());
-
- EXPECT_EQ(kConvertRoundtripCases[i], wide.str());
- }
-}
-
-TEST(UTFStringConversionsTest, ConvertUTF8AndWideEmptyString) {
- // An empty std::wstring should be converted to an empty std::string,
- // and vice versa.
- std::wstring wempty;
- std::string empty;
- EXPECT_EQ(empty, WideToUTF8(wempty));
- EXPECT_EQ(wempty, UTF8ToWide(empty));
-}
-
-TEST(UTFStringConversionsTest, ConvertUTF8ToWide) {
- struct UTF8ToWideCase {
- const char* utf8;
- const wchar_t* wide;
- bool success;
- } convert_cases[] = {
- // Regular UTF-8 input.
- {"\xe4\xbd\xa0\xe5\xa5\xbd", L"\x4f60\x597d", true},
- // Non-character is passed through.
- {"\xef\xbf\xbfHello", L"\xffffHello", true},
- // Truncated UTF-8 sequence.
- {"\xe4\xa0\xe5\xa5\xbd", L"\xfffd\x597d", false},
- // Truncated off the end.
- {"\xe5\xa5\xbd\xe4\xa0", L"\x597d\xfffd", false},
- // Non-shortest-form UTF-8.
- {"\xf0\x84\xbd\xa0\xe5\xa5\xbd", L"\xfffd\x597d", false},
- // This UTF-8 character decodes to a UTF-16 surrogate, which is illegal.
- // Note that for whatever reason, this test fails on Windows XP.
- {"\xed\xb0\x80", L"\xfffd", false},
- // Non-BMP characters. The second is a non-character regarded as valid.
- // The result will either be in UTF-16 or UTF-32.
-#if defined(WCHAR_T_IS_UTF16)
- {"A\xF0\x90\x8C\x80z", L"A\xd800\xdf00z", true},
- {"A\xF4\x8F\xBF\xBEz", L"A\xdbff\xdffez", true},
-#elif defined(WCHAR_T_IS_UTF32)
- {"A\xF0\x90\x8C\x80z", L"A\x10300z", true},
- {"A\xF4\x8F\xBF\xBEz", L"A\x10fffez", true},
-#endif
- };
-
- for (size_t i = 0; i < arraysize(convert_cases); i++) {
- std::wstring converted;
- errno = 0;
- const bool success = UTF8ToWide(convert_cases[i].utf8,
- strlen(convert_cases[i].utf8),
- &converted);
- EXPECT_EQ(convert_cases[i].success, success);
- // The original test always compared expected and converted, but don't do
- // that because our implementation of UTF8ToWide() does not guarantee to
- // produce the same output in error situations.
- if (success) {
- std::wstring expected(convert_cases[i].wide);
- EXPECT_EQ(expected, converted);
- } else {
- EXPECT_EQ(EILSEQ, errno);
- }
- }
-
- // Manually test an embedded NULL.
- std::wstring converted;
- EXPECT_TRUE(UTF8ToWide("\00Z\t", 3, &converted));
- ASSERT_EQ(3U, converted.length());
- EXPECT_EQ(static_cast<wchar_t>(0), converted[0]);
- EXPECT_EQ('Z', converted[1]);
- EXPECT_EQ('\t', converted[2]);
-
- // Make sure that conversion replaces, not appends.
- EXPECT_TRUE(UTF8ToWide("B", 1, &converted));
- ASSERT_EQ(1U, converted.length());
- EXPECT_EQ('B', converted[0]);
-}
-
-#if defined(WCHAR_T_IS_UTF16)
-// This test is only valid when wchar_t == UTF-16.
-TEST(UTFStringConversionsTest, ConvertUTF16ToUTF8) {
- struct WideToUTF8Case {
- const wchar_t* utf16;
- const char* utf8;
- bool success;
- } convert_cases[] = {
- // Regular UTF-16 input.
- {L"\x4f60\x597d", "\xe4\xbd\xa0\xe5\xa5\xbd", true},
- // Test a non-BMP character.
- {L"\xd800\xdf00", "\xF0\x90\x8C\x80", true},
- // Non-characters are passed through.
- {L"\xffffHello", "\xEF\xBF\xBFHello", true},
- {L"\xdbff\xdffeHello", "\xF4\x8F\xBF\xBEHello", true},
- // The first character is a truncated UTF-16 character.
- // Note that for whatever reason, this test fails on Windows XP.
- {L"\xd800\x597d", "\xef\xbf\xbd\xe5\xa5\xbd",
-#if (WINVER >= 0x0600)
- // Only Vista and later has a new API/flag that correctly returns false.
- false
-#else
- true
-#endif
- },
- // Truncated at the end.
- // Note that for whatever reason, this test fails on Windows XP.
- {L"\x597d\xd800", "\xe5\xa5\xbd\xef\xbf\xbd",
-#if (WINVER >= 0x0600)
- // Only Vista and later has a new API/flag that correctly returns false.
- false
-#else
- true
-#endif
- },
- };
-
- for (size_t i = 0; i < arraysize(convert_cases); i++) {
- std::string converted;
- errno = 0;
- const bool success = WideToUTF8(convert_cases[i].utf16,
- wcslen(convert_cases[i].utf16),
- &converted);
- EXPECT_EQ(convert_cases[i].success, success);
- // The original test always compared expected and converted, but don't do
- // that because our implementation of WideToUTF8() does not guarantee to
- // produce the same output in error situations.
- if (success) {
- std::string expected(convert_cases[i].utf8);
- EXPECT_EQ(expected, converted);
- } else {
- EXPECT_EQ(EILSEQ, errno);
- }
- }
-}
-
-#elif defined(WCHAR_T_IS_UTF32)
-// This test is only valid when wchar_t == UTF-32.
-TEST(UTFStringConversionsTest, ConvertUTF32ToUTF8) {
- struct WideToUTF8Case {
- const wchar_t* utf32;
- const char* utf8;
- bool success;
- } convert_cases[] = {
- // Regular 16-bit input.
- {L"\x4f60\x597d", "\xe4\xbd\xa0\xe5\xa5\xbd", true},
- // Test a non-BMP character.
- {L"A\x10300z", "A\xF0\x90\x8C\x80z", true},
- // Non-characters are passed through.
- {L"\xffffHello", "\xEF\xBF\xBFHello", true},
- {L"\x10fffeHello", "\xF4\x8F\xBF\xBEHello", true},
- // Invalid Unicode code points.
- {L"\xfffffffHello", "\xEF\xBF\xBDHello", false},
- // The first character is a truncated UTF-16 character.
- {L"\xd800\x597d", "\xef\xbf\xbd\xe5\xa5\xbd", false},
- {L"\xdc01Hello", "\xef\xbf\xbdHello", false},
- };
-
- for (size_t i = 0; i < arraysize(convert_cases); i++) {
- std::string converted;
- EXPECT_EQ(convert_cases[i].success,
- WideToUTF8(convert_cases[i].utf32,
- wcslen(convert_cases[i].utf32),
- &converted));
- std::string expected(convert_cases[i].utf8);
- EXPECT_EQ(expected, converted);
- }
-}
-#endif // defined(WCHAR_T_IS_UTF32)
-
-// The test below uses these types and functions, so just do enough to get the
-// test running.
-typedef wchar_t char16;
-typedef std::wstring string16;
-
-template<typename T>
-static void* WriteInto(T* t, size_t size) {
- // std::(w)string::resize() already includes space for a NULL terminator.
- t->resize(size - 1);
- return &(*t)[0];
-}
-
-// A stub implementation that calls a helper from above, just to get the test
-// below working. This is just for testing and should not be moved to base
-// because this ignores errors which is probably not a good idea, plus it takes
-// a string16 type which we don't really have.
-static std::string UTF16ToUTF8(const string16& utf16) {
- return WideToUTF8(utf16);
-}
-
-TEST(UTFStringConversionsTest, ConvertMultiString) {
- static char16 multi16[] = {
- 'f', 'o', 'o', '\0',
- 'b', 'a', 'r', '\0',
- 'b', 'a', 'z', '\0',
- '\0'
- };
- static char multi[] = {
- 'f', 'o', 'o', '\0',
- 'b', 'a', 'r', '\0',
- 'b', 'a', 'z', '\0',
- '\0'
- };
- string16 multistring16;
- memcpy(WriteInto(&multistring16, arraysize(multi16)), multi16,
- sizeof(multi16));
- EXPECT_EQ(arraysize(multi16) - 1, multistring16.length());
- std::string expected;
- memcpy(WriteInto(&expected, arraysize(multi)), multi, sizeof(multi));
- EXPECT_EQ(arraysize(multi) - 1, expected.length());
- const std::string& converted = UTF16ToUTF8(multistring16);
- EXPECT_EQ(arraysize(multi) - 1, converted.length());
- EXPECT_EQ(expected, converted);
-}
-
-// The tests below from sys_string_conversions_unittest.cc call SysWideToUTF8()
-// and SysUTF8ToWide(), so these are stub implementations that call the helpers
-// above. These are just for testing and should not be moved to base because
-// they ignore errors which is probably not a good idea.
-
-static std::string SysWideToUTF8(const std::wstring& utf16) {
- return WideToUTF8(utf16);
-}
-
-static std::wstring SysUTF8ToWide(const std::string& utf8) {
- return UTF8ToWide(utf8);
-}
-
-// Below is adapted from https://chromium.googlesource.com/chromium/src/+/master/base/strings/sys_string_conversions_unittest.cc
-
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifdef WCHAR_T_IS_UTF32
-static const std::wstring kSysWideOldItalicLetterA = L"\x10300";
-#else
-static const std::wstring kSysWideOldItalicLetterA = L"\xd800\xdf00";
-#endif
-
-TEST(SysStrings, SysWideToUTF8) {
- EXPECT_EQ("Hello, world", SysWideToUTF8(L"Hello, world"));
- EXPECT_EQ("\xe4\xbd\xa0\xe5\xa5\xbd", SysWideToUTF8(L"\x4f60\x597d"));
-
- // >16 bits
- EXPECT_EQ("\xF0\x90\x8C\x80", SysWideToUTF8(kSysWideOldItalicLetterA));
-
- // Error case. When Windows finds a UTF-16 character going off the end of
- // a string, it just converts that literal value to UTF-8, even though this
- // is invalid.
- //
- // This is what XP does, but Vista has different behavior, so we don't bother
- // verifying it:
- // EXPECT_EQ("\xE4\xBD\xA0\xED\xA0\x80zyxw",
- // SysWideToUTF8(L"\x4f60\xd800zyxw"));
-
- // Test embedded NULLs.
- std::wstring wide_null(L"a");
- wide_null.push_back(0);
- wide_null.push_back('b');
-
- std::string expected_null("a");
- expected_null.push_back(0);
- expected_null.push_back('b');
-
- EXPECT_EQ(expected_null, SysWideToUTF8(wide_null));
-}
-
-TEST(SysStrings, SysUTF8ToWide) {
- EXPECT_EQ(L"Hello, world", SysUTF8ToWide("Hello, world"));
- EXPECT_EQ(L"\x4f60\x597d", SysUTF8ToWide("\xe4\xbd\xa0\xe5\xa5\xbd"));
- // >16 bits
- EXPECT_EQ(kSysWideOldItalicLetterA, SysUTF8ToWide("\xF0\x90\x8C\x80"));
-
- // Error case. When Windows finds an invalid UTF-8 character, it just skips
- // it. This seems weird because it's inconsistent with the reverse conversion.
- //
- // This is what XP does, but Vista has different behavior, so we don't bother
- // verifying it:
- // EXPECT_EQ(L"\x4f60zyxw", SysUTF8ToWide("\xe4\xbd\xa0\xe5\xa5zyxw"));
-
- // Test embedded NULLs.
- std::string utf8_null("a");
- utf8_null.push_back(0);
- utf8_null.push_back('b');
-
- std::wstring expected_null(L"a");
- expected_null.push_back(0);
- expected_null.push_back('b');
-
- EXPECT_EQ(expected_null, SysUTF8ToWide(utf8_null));
-}
-
-TEST(UTF8PathToWindowsLongPathTest, DontAddPrefixIfShorterThanMaxPath) {
- std::string utf8 = "c:\\mypath\\myfile.txt";
-
- std::wstring wide;
- EXPECT_TRUE(UTF8PathToWindowsLongPath(utf8.c_str(), &wide));
-
- EXPECT_EQ(std::string::npos, wide.find(LR"(\\?\)"));
-}
-
-TEST(UTF8PathToWindowsLongPathTest, AddPrefixIfLongerThanMaxPath) {
- std::string utf8 = "c:\\mypath";
- while (utf8.length() < 300 /* MAX_PATH is 260 */) {
- utf8 += "\\mypathsegment";
- }
-
- std::wstring wide;
- EXPECT_TRUE(UTF8PathToWindowsLongPath(utf8.c_str(), &wide));
-
- EXPECT_EQ(0U, wide.find(LR"(\\?\)"));
- EXPECT_EQ(std::string::npos, wide.find(L"/"));
-}
-
-TEST(UTF8PathToWindowsLongPathTest, AddPrefixAndFixSeparatorsIfLongerThanMaxPath) {
- std::string utf8 = "c:/mypath";
- while (utf8.length() < 300 /* MAX_PATH is 260 */) {
- utf8 += "/mypathsegment";
- }
-
- std::wstring wide;
- EXPECT_TRUE(UTF8PathToWindowsLongPath(utf8.c_str(), &wide));
-
- EXPECT_EQ(0U, wide.find(LR"(\\?\)"));
- EXPECT_EQ(std::string::npos, wide.find(L"/"));
-}
-
-namespace utf8 {
-
-TEST(Utf8FilesTest, CanCreateOpenAndDeleteFileWithLongPath) {
- TemporaryDir td;
-
- // Create long directory path
- std::string utf8 = td.path;
- while (utf8.length() < 300 /* MAX_PATH is 260 */) {
- utf8 += "\\mypathsegment";
- EXPECT_EQ(0, mkdir(utf8.c_str(), 0));
- }
-
- // Create file
- utf8 += "\\test-file.bin";
- int flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
- int mode = 0666;
- android::base::unique_fd fd(open(utf8.c_str(), flags, mode));
- EXPECT_NE(-1, fd.get());
-
- // Close file
- fd.reset();
- EXPECT_EQ(-1, fd.get());
-
- // Open file with fopen
- FILE* file = fopen(utf8.c_str(), "rb");
- EXPECT_NE(nullptr, file);
-
- if (file) {
- fclose(file);
- }
-
- // Delete file
- EXPECT_EQ(0, unlink(utf8.c_str()));
-}
-
-} // namespace utf8
-} // namespace base
-} // namespace android
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index a9c1676..5d6cee4 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -1238,16 +1238,26 @@
// Shift last_reboot_reason_property to last_last_reboot_reason_property
std::string last_boot_reason;
if (!android::base::ReadFileToString(last_reboot_reason_file, &last_boot_reason)) {
+ PLOG(ERROR) << "Failed to read " << last_reboot_reason_file;
last_boot_reason = android::base::GetProperty(last_reboot_reason_property, "");
+ LOG(INFO) << "Value of " << last_reboot_reason_property << " : " << last_boot_reason;
+ } else {
+ LOG(INFO) << "Last reboot reason read from " << last_reboot_reason_file << " : "
+ << last_boot_reason << ". Last reboot reason read from "
+ << last_reboot_reason_property << " : "
+ << android::base::GetProperty(last_reboot_reason_property, "");
}
if (last_boot_reason.empty() || isKernelRebootReason(system_boot_reason)) {
last_boot_reason = system_boot_reason;
} else {
transformReason(last_boot_reason);
}
+ LOG(INFO) << "Normalized last reboot reason : " << last_boot_reason;
android::base::SetProperty(last_last_reboot_reason_property, last_boot_reason);
android::base::SetProperty(last_reboot_reason_property, "");
- unlink(last_reboot_reason_file);
+ if (unlink(last_reboot_reason_file) != 0) {
+ PLOG(ERROR) << "Failed to unlink " << last_reboot_reason_file;
+ }
}
// Gets the boot time offset. This is useful when Android is running in a
diff --git a/cpio/mkbootfs.c b/cpio/mkbootfs.c
index e52762e..58153f3 100644
--- a/cpio/mkbootfs.c
+++ b/cpio/mkbootfs.c
@@ -13,6 +13,7 @@
#include <fcntl.h>
#include <private/android_filesystem_config.h>
+#include <private/fs_config.h>
/* NOTES
**
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index f28c778..31c2d5d 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -103,9 +103,14 @@
export_include_dirs: ["include"],
}
-// Fallback implementation.
+// Fallback implementation, for use in the Bionic linker only.
cc_library_static {
name: "libdebuggerd_handler_fallback",
+ visibility: ["//bionic/linker"],
+ apex_available: [
+ "com.android.runtime",
+ "//apex_available:platform",
+ ],
defaults: ["debuggerd_defaults"],
recovery_available: true,
srcs: [
@@ -118,8 +123,7 @@
"libasync_safe",
"libbase",
"libdebuggerd",
- "libunwindstack",
- "libdexfile_support_static", // libunwindstack dependency
+ "libunwindstack_no_dex",
"liblzma",
"libcutils",
],
@@ -127,14 +131,6 @@
header_libs: ["bionic_libc_platform_headers"],
export_header_lib_headers: ["bionic_libc_platform_headers"],
- target: {
- recovery: {
- exclude_static_libs: [
- "libdexfile_support_static",
- ],
- },
- },
-
export_include_dirs: ["include"],
}
@@ -173,6 +169,7 @@
"libdebuggerd/backtrace.cpp",
"libdebuggerd/gwp_asan.cpp",
"libdebuggerd/open_files_list.cpp",
+ "libdebuggerd/scudo.cpp",
"libdebuggerd/tombstone.cpp",
"libdebuggerd/utility.cpp",
],
@@ -180,15 +177,20 @@
local_include_dirs: ["libdebuggerd/include"],
export_include_dirs: ["libdebuggerd/include"],
- // Needed for private/bionic_fdsan.h
- include_dirs: ["bionic/libc"],
+ include_dirs: [
+ // Needed for private/bionic_fdsan.h
+ "bionic/libc",
+
+ // Needed for scudo/interface.h
+ "external/scudo/standalone/include",
+ ],
header_libs: [
"bionic_libc_platform_headers",
"gwp_asan_headers",
],
static_libs: [
- "libdexfile_support_static", // libunwindstack dependency
+ "libdexfile_support", // libunwindstack dependency
"libunwindstack",
"liblzma",
"libbase",
@@ -196,12 +198,15 @@
"liblog",
],
- whole_static_libs: ["gwp_asan_crash_handler"],
+ whole_static_libs: [
+ "gwp_asan_crash_handler",
+ "libscudo",
+ ],
target: {
recovery: {
exclude_static_libs: [
- "libdexfile_support_static",
+ "libdexfile_support",
],
},
},
@@ -210,6 +215,9 @@
debuggable: {
cflags: ["-DROOT_POSSIBLE"],
},
+ experimental_mte: {
+ cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
+ },
},
}
@@ -260,6 +268,10 @@
"gwp_asan_headers",
],
+ include_dirs: [
+ "external/scudo/standalone/include",
+ ],
+
local_include_dirs: [
"libdebuggerd",
],
@@ -275,6 +287,12 @@
},
test_suites: ["device-tests"],
+
+ product_variables: {
+ experimental_mte: {
+ cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
+ },
+ },
}
cc_benchmark {
@@ -321,6 +339,10 @@
"libprocinfo",
"libunwindstack",
],
+
+ apex_available: [
+ "com.android.runtime",
+ ],
}
cc_binary {
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 3e99880..d7cb972 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -254,9 +254,7 @@
}
static void ReadCrashInfo(unique_fd& fd, siginfo_t* siginfo,
- std::unique_ptr<unwindstack::Regs>* regs, uintptr_t* abort_msg_address,
- uintptr_t* fdsan_table_address, uintptr_t* gwp_asan_state,
- uintptr_t* gwp_asan_metadata) {
+ std::unique_ptr<unwindstack::Regs>* regs, ProcessInfo* process_info) {
std::aligned_storage<sizeof(CrashInfo) + 1, alignof(CrashInfo)>::type buf;
CrashInfo* crash_info = reinterpret_cast<CrashInfo*>(&buf);
ssize_t rc = TEMP_FAILURE_RETRY(read(fd.get(), &buf, sizeof(buf)));
@@ -266,15 +264,13 @@
ssize_t expected_size = 0;
switch (crash_info->header.version) {
case 1:
- expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataV1);
- break;
-
case 2:
- expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataV2);
+ case 3:
+ expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataStatic);
break;
- case 3:
- expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataV3);
+ case 4:
+ expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataDynamic);
break;
default:
@@ -282,28 +278,34 @@
break;
};
- if (rc != expected_size) {
+ if (rc < expected_size) {
LOG(FATAL) << "read " << rc << " bytes when reading target crash information, expected "
<< expected_size;
}
}
- *fdsan_table_address = 0;
- *gwp_asan_state = 0;
- *gwp_asan_metadata = 0;
switch (crash_info->header.version) {
- case 3:
- *gwp_asan_state = crash_info->data.v3.gwp_asan_state;
- *gwp_asan_metadata = crash_info->data.v3.gwp_asan_metadata;
- FALLTHROUGH_INTENDED;
- case 2:
- *fdsan_table_address = crash_info->data.v2.fdsan_table_address;
+ case 4:
+ process_info->fdsan_table_address = crash_info->data.d.fdsan_table_address;
+ process_info->gwp_asan_state = crash_info->data.d.gwp_asan_state;
+ process_info->gwp_asan_metadata = crash_info->data.d.gwp_asan_metadata;
+ process_info->scudo_stack_depot = crash_info->data.d.scudo_stack_depot;
+ process_info->scudo_region_info = crash_info->data.d.scudo_region_info;
FALLTHROUGH_INTENDED;
case 1:
- *abort_msg_address = crash_info->data.v1.abort_msg_address;
- *siginfo = crash_info->data.v1.siginfo;
+ case 2:
+ case 3:
+ process_info->abort_msg_address = crash_info->data.s.abort_msg_address;
+ *siginfo = crash_info->data.s.siginfo;
+ if (signal_has_si_addr(siginfo)) {
+ // Make a copy of the ucontext field because otherwise it is not aligned enough (due to
+ // being in a packed struct) and clang complains about that.
+ ucontext_t ucontext = crash_info->data.s.ucontext;
+ process_info->has_fault_address = true;
+ process_info->fault_address = get_fault_address(siginfo, &ucontext);
+ }
regs->reset(unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(),
- &crash_info->data.v1.ucontext));
+ &crash_info->data.s.ucontext));
break;
default:
@@ -425,10 +427,7 @@
ATRACE_NAME("after reparent");
pid_t pseudothread_tid;
DebuggerdDumpType dump_type;
- uintptr_t abort_msg_address = 0;
- uintptr_t fdsan_table_address = 0;
- uintptr_t gwp_asan_state = 0;
- uintptr_t gwp_asan_metadata = 0;
+ ProcessInfo process_info;
Initialize(argv);
ParseArgs(argc, argv, &pseudothread_tid, &dump_type);
@@ -489,8 +488,7 @@
if (thread == g_target_thread) {
// Read the thread's registers along with the rest of the crash info out of the pipe.
- ReadCrashInfo(input_pipe, &siginfo, &info.registers, &abort_msg_address,
- &fdsan_table_address, &gwp_asan_state, &gwp_asan_metadata);
+ ReadCrashInfo(input_pipe, &siginfo, &info.registers, &process_info);
info.siginfo = &siginfo;
info.signo = info.siginfo->si_signo;
} else {
@@ -599,14 +597,14 @@
} else {
{
ATRACE_NAME("fdsan table dump");
- populate_fdsan_table(&open_files, unwinder.GetProcessMemory(), fdsan_table_address);
+ populate_fdsan_table(&open_files, unwinder.GetProcessMemory(),
+ process_info.fdsan_table_address);
}
{
ATRACE_NAME("engrave_tombstone");
- engrave_tombstone(std::move(g_output_fd), &unwinder, thread_info, g_target_thread,
- abort_msg_address, &open_files, &amfd_data, gwp_asan_state,
- gwp_asan_metadata);
+ engrave_tombstone(std::move(g_output_fd), &unwinder, thread_info, g_target_thread, process_info,
+ &open_files, &amfd_data);
}
}
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
index 3041664..a2b13a3 100644
--- a/debuggerd/crasher/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -349,7 +349,7 @@
int main(int argc, char** argv) {
#if defined(STATIC_CRASHER)
debuggerd_callbacks_t callbacks = {
- .get_abort_message = []() {
+ .get_process_info = []() {
static struct {
size_t size;
char msg[32];
@@ -357,7 +357,9 @@
msg.size = strlen("dummy abort message");
memcpy(msg.msg, "dummy abort message", strlen("dummy abort message"));
- return reinterpret_cast<abort_msg_t*>(&msg);
+ return debugger_process_info{
+ .abort_msg = reinterpret_cast<void*>(&msg),
+ };
},
.post_dump = nullptr
};
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 6a8cc56..9d7658e 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -31,6 +31,9 @@
#include <android/fdsan.h>
#include <android/set_abort_message.h>
+#include <bionic/malloc.h>
+#include <bionic/mte.h>
+#include <bionic/mte_kernel.h>
#include <bionic/reserved_signals.h>
#include <android-base/cmsg.h>
@@ -305,6 +308,210 @@
ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr 0xdead)");
}
+TEST_F(CrasherTest, tagged_fault_addr) {
+#if !defined(__aarch64__)
+ GTEST_SKIP() << "Requires aarch64";
+#endif
+ int intercept_result;
+ unique_fd output_fd;
+ StartProcess([]() {
+ *reinterpret_cast<volatile char*>(0x100000000000dead) = '1';
+ });
+
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGSEGV);
+ FinishIntercept(&intercept_result);
+
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+
+ // The address can either be tagged (new kernels) or untagged (old kernels).
+ ASSERT_MATCH(
+ result,
+ R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr (0x100000000000dead|0xdead))");
+}
+
+#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
+static void SetTagCheckingLevelSync() {
+ int tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
+ if (tagged_addr_ctrl < 0) {
+ abort();
+ }
+
+ tagged_addr_ctrl = (tagged_addr_ctrl & ~PR_MTE_TCF_MASK) | PR_MTE_TCF_SYNC;
+ if (prctl(PR_SET_TAGGED_ADDR_CTRL, tagged_addr_ctrl, 0, 0, 0) != 0) {
+ abort();
+ }
+
+ HeapTaggingLevel heap_tagging_level = M_HEAP_TAGGING_LEVEL_SYNC;
+ if (!android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &heap_tagging_level, sizeof(heap_tagging_level))) {
+ abort();
+ }
+}
+#endif
+
+TEST_F(CrasherTest, mte_uaf) {
+#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
+ if (!mte_supported()) {
+ GTEST_SKIP() << "Requires MTE";
+ }
+
+ int intercept_result;
+ unique_fd output_fd;
+ StartProcess([]() {
+ SetTagCheckingLevelSync();
+ volatile int* p = (volatile int*)malloc(16);
+ free((void *)p);
+ p[0] = 42;
+ });
+
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGSEGV);
+ FinishIntercept(&intercept_result);
+
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+
+ ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 9 \(SEGV_MTESERR\))");
+ ASSERT_MATCH(result, R"(Cause: \[MTE\]: Use After Free, 0 bytes into a 16-byte allocation.*
+
+allocated by thread .*
+ #00 pc)");
+ ASSERT_MATCH(result, R"(deallocated by thread .*
+ #00 pc)");
+#else
+ GTEST_SKIP() << "Requires aarch64 + ANDROID_EXPERIMENTAL_MTE";
+#endif
+}
+
+TEST_F(CrasherTest, mte_overflow) {
+#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
+ if (!mte_supported()) {
+ GTEST_SKIP() << "Requires MTE";
+ }
+
+ int intercept_result;
+ unique_fd output_fd;
+ StartProcess([]() {
+ SetTagCheckingLevelSync();
+ volatile int* p = (volatile int*)malloc(16);
+ p[4] = 42;
+ });
+
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGSEGV);
+ FinishIntercept(&intercept_result);
+
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+
+ ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\))");
+ ASSERT_MATCH(result, R"(Cause: \[MTE\]: Buffer Overflow, 0 bytes right of a 16-byte allocation.*
+
+allocated by thread .*
+ #00 pc)");
+#else
+ GTEST_SKIP() << "Requires aarch64 + ANDROID_EXPERIMENTAL_MTE";
+#endif
+}
+
+TEST_F(CrasherTest, mte_underflow) {
+#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
+ if (!mte_supported()) {
+ GTEST_SKIP() << "Requires MTE";
+ }
+
+ int intercept_result;
+ unique_fd output_fd;
+ StartProcess([]() {
+ SetTagCheckingLevelSync();
+ volatile int* p = (volatile int*)malloc(16);
+ p[-1] = 42;
+ });
+
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGSEGV);
+ FinishIntercept(&intercept_result);
+
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+
+ ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 9 \(SEGV_MTESERR\))");
+ ASSERT_MATCH(result, R"(Cause: \[MTE\]: Buffer Underflow, 4 bytes left of a 16-byte allocation.*
+
+allocated by thread .*
+ #00 pc)");
+#else
+ GTEST_SKIP() << "Requires aarch64 + ANDROID_EXPERIMENTAL_MTE";
+#endif
+}
+
+TEST_F(CrasherTest, mte_multiple_causes) {
+#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
+ if (!mte_supported()) {
+ GTEST_SKIP() << "Requires MTE";
+ }
+
+ int intercept_result;
+ unique_fd output_fd;
+ StartProcess([]() {
+ SetTagCheckingLevelSync();
+
+ // Make two allocations with the same tag and close to one another. Check for both properties
+ // with a bounds check -- this relies on the fact that only if the allocations have the same tag
+ // would they be measured as closer than 128 bytes to each other. Otherwise they would be about
+ // (some non-zero value << 56) apart.
+ //
+ // The out-of-bounds access will be considered either an overflow of one or an underflow of the
+ // other.
+ std::set<uintptr_t> allocs;
+ for (int i = 0; i != 4096; ++i) {
+ uintptr_t alloc = reinterpret_cast<uintptr_t>(malloc(16));
+ auto it = allocs.insert(alloc).first;
+ if (it != allocs.begin() && *std::prev(it) + 128 > alloc) {
+ *reinterpret_cast<int*>(*std::prev(it) + 16) = 42;
+ }
+ if (std::next(it) != allocs.end() && alloc + 128 > *std::next(it)) {
+ *reinterpret_cast<int*>(alloc + 16) = 42;
+ }
+ }
+ });
+
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGSEGV);
+ FinishIntercept(&intercept_result);
+
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+
+ ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\))");
+ ASSERT_MATCH(
+ result,
+ R"(Note: multiple potential causes for this crash were detected, listing them in decreasing order of probability.)");
+
+ // Adjacent untracked allocations may cause us to see the wrong underflow here (or only
+ // overflows), so we can't match explicitly for an underflow message.
+ ASSERT_MATCH(result, R"(Cause: \[MTE\]: Buffer Overflow, 0 bytes right of a 16-byte allocation)");
+#else
+ GTEST_SKIP() << "Requires aarch64 + ANDROID_EXPERIMENTAL_MTE";
+#endif
+}
+
TEST_F(CrasherTest, LD_PRELOAD) {
int intercept_result;
unique_fd output_fd;
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index 8b4b630..121a074 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -83,7 +83,7 @@
#define CRASH_DUMP_NAME "crash_dump32"
#endif
-#define CRASH_DUMP_PATH "/system/bin/" CRASH_DUMP_NAME
+#define CRASH_DUMP_PATH "/apex/com.android.runtime/bin/" CRASH_DUMP_NAME
// Wrappers that directly invoke the respective syscalls, in case the cached values are invalid.
#pragma GCC poison getpid gettid
@@ -167,7 +167,7 @@
* mutex is being held, so we don't want to use any libc functions that
* could allocate memory or hold a lock.
*/
-static void log_signal_summary(const siginfo_t* info) {
+static void log_signal_summary(const siginfo_t* info, const ucontext_t* ucontext) {
char thread_name[MAX_TASK_NAME_LEN + 1]; // one more for termination
if (prctl(PR_GET_NAME, reinterpret_cast<unsigned long>(thread_name), 0, 0, 0) != 0) {
strcpy(thread_name, "<name unknown>");
@@ -186,7 +186,8 @@
// Many signals don't have an address or sender.
char addr_desc[32] = ""; // ", fault addr 0x1234"
if (signal_has_si_addr(info)) {
- async_safe_format_buffer(addr_desc, sizeof(addr_desc), ", fault addr %p", info->si_addr);
+ async_safe_format_buffer(addr_desc, sizeof(addr_desc), ", fault addr %p",
+ reinterpret_cast<void*>(get_fault_address(info, ucontext)));
}
pid_t self_pid = __getpid();
char sender_desc[32] = {}; // " from pid 1234, uid 666"
@@ -297,10 +298,7 @@
pid_t pseudothread_tid;
siginfo_t* siginfo;
void* ucontext;
- uintptr_t abort_msg;
- uintptr_t fdsan_table;
- uintptr_t gwp_asan_state;
- uintptr_t gwp_asan_metadata;
+ debugger_process_info process_info;
};
// Logging and contacting debuggerd requires free file descriptors, which we might not have.
@@ -344,25 +342,36 @@
fatal_errno("failed to create pipe");
}
- // ucontext_t is absurdly large on AArch64, so piece it together manually with writev.
- uint32_t version = 3;
- constexpr size_t expected = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataV3);
+ uint32_t version;
+ ssize_t expected;
+ // ucontext_t is absurdly large on AArch64, so piece it together manually with writev.
+ struct iovec iovs[4] = {
+ {.iov_base = &version, .iov_len = sizeof(version)},
+ {.iov_base = thread_info->siginfo, .iov_len = sizeof(siginfo_t)},
+ {.iov_base = thread_info->ucontext, .iov_len = sizeof(ucontext_t)},
+ };
+
+ if (thread_info->process_info.fdsan_table) {
+ // Dynamic executables always use version 4. There is no need to increment the version number if
+ // the format changes, because the sender (linker) and receiver (crash_dump) are version locked.
+ version = 4;
+ expected = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataDynamic);
+
+ iovs[3] = {.iov_base = &thread_info->process_info,
+ .iov_len = sizeof(thread_info->process_info)};
+ } else {
+ // Static executables always use version 1.
+ version = 1;
+ expected = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataStatic);
+
+ iovs[3] = {.iov_base = &thread_info->process_info.abort_msg, .iov_len = sizeof(uintptr_t)};
+ }
errno = 0;
if (fcntl(output_write.get(), F_SETPIPE_SZ, expected) < static_cast<int>(expected)) {
fatal_errno("failed to set pipe buffer size");
}
- struct iovec iovs[] = {
- {.iov_base = &version, .iov_len = sizeof(version)},
- {.iov_base = thread_info->siginfo, .iov_len = sizeof(siginfo_t)},
- {.iov_base = thread_info->ucontext, .iov_len = sizeof(ucontext_t)},
- {.iov_base = &thread_info->abort_msg, .iov_len = sizeof(uintptr_t)},
- {.iov_base = &thread_info->fdsan_table, .iov_len = sizeof(uintptr_t)},
- {.iov_base = &thread_info->gwp_asan_state, .iov_len = sizeof(uintptr_t)},
- {.iov_base = &thread_info->gwp_asan_metadata, .iov_len = sizeof(uintptr_t)},
- };
-
ssize_t rc = TEMP_FAILURE_RETRY(writev(output_write.get(), iovs, arraysize(iovs)));
if (rc == -1) {
fatal_errno("failed to write crash info");
@@ -408,23 +417,28 @@
// us to fork off a process to read memory from.
char buf[4];
rc = TEMP_FAILURE_RETRY(read(input_read.get(), &buf, sizeof(buf)));
- if (rc == -1) {
- async_safe_format_log(ANDROID_LOG_FATAL, "libc", "read of IPC pipe failed: %s", strerror(errno));
- return 1;
- } else if (rc == 0) {
- async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper failed to exec");
- return 1;
- } else if (rc != 1) {
- async_safe_format_log(ANDROID_LOG_FATAL, "libc",
- "read of IPC pipe returned unexpected value: %zd", rc);
- return 1;
- } else if (buf[0] != '\1') {
- async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper reported failure");
- return 1;
- }
- // crash_dump is ptracing us, fork off a copy of our address space for it to use.
- create_vm_process();
+ bool success = false;
+ if (rc == 1 && buf[0] == '\1') {
+ // crash_dump successfully started, and is ptracing us.
+ // Fork off a copy of our address space for it to use.
+ create_vm_process();
+ success = true;
+ } else {
+ // Something went wrong, log it.
+ if (rc == -1) {
+ async_safe_format_log(ANDROID_LOG_FATAL, "libc", "read of IPC pipe failed: %s",
+ strerror(errno));
+ } else if (rc == 0) {
+ async_safe_format_log(ANDROID_LOG_FATAL, "libc",
+ "crash_dump helper failed to exec, or was killed");
+ } else if (rc != 1) {
+ async_safe_format_log(ANDROID_LOG_FATAL, "libc",
+ "read of IPC pipe returned unexpected value: %zd", rc);
+ } else if (buf[0] != '\1') {
+ async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper reported failure");
+ }
+ }
// Don't leave a zombie child.
int status;
@@ -435,14 +449,16 @@
async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper crashed or stopped");
}
- if (thread_info->siginfo->si_signo != BIONIC_SIGNAL_DEBUGGER) {
- // For crashes, we don't need to minimize pause latency.
- // Wait for the dump to complete before having the process exit, to avoid being murdered by
- // ActivityManager or init.
- TEMP_FAILURE_RETRY(read(input_read, &buf, sizeof(buf)));
+ if (success) {
+ if (thread_info->siginfo->si_signo != BIONIC_SIGNAL_DEBUGGER) {
+ // For crashes, we don't need to minimize pause latency.
+ // Wait for the dump to complete before having the process exit, to avoid being murdered by
+ // ActivityManager or init.
+ TEMP_FAILURE_RETRY(read(input_read, &buf, sizeof(buf)));
+ }
}
- return 0;
+ return success ? 0 : 1;
}
static void resend_signal(siginfo_t* info) {
@@ -468,6 +484,8 @@
// making a syscall and checking errno.
ErrnoRestorer restorer;
+ auto *ucontext = static_cast<ucontext_t*>(context);
+
// It's possible somebody cleared the SA_SIGINFO flag, which would mean
// our "info" arg holds an undefined value.
if (!have_siginfo(signal_number)) {
@@ -489,29 +507,19 @@
// check to allow all si_code values in calls coming from inside the house.
}
- void* abort_message = nullptr;
- const gwp_asan::AllocatorState* gwp_asan_state = nullptr;
- const gwp_asan::AllocationMetadata* gwp_asan_metadata = nullptr;
+ debugger_process_info process_info = {};
uintptr_t si_val = reinterpret_cast<uintptr_t>(info->si_ptr);
if (signal_number == BIONIC_SIGNAL_DEBUGGER) {
if (info->si_code == SI_QUEUE && info->si_pid == __getpid()) {
// Allow for the abort message to be explicitly specified via the sigqueue value.
// Keep the bottom bit intact for representing whether we want a backtrace or a tombstone.
if (si_val != kDebuggerdFallbackSivalUintptrRequestDump) {
- abort_message = reinterpret_cast<void*>(si_val & ~1);
+ process_info.abort_msg = reinterpret_cast<void*>(si_val & ~1);
info->si_ptr = reinterpret_cast<void*>(si_val & 1);
}
}
- } else {
- if (g_callbacks.get_abort_message) {
- abort_message = g_callbacks.get_abort_message();
- }
- if (g_callbacks.get_gwp_asan_state) {
- gwp_asan_state = g_callbacks.get_gwp_asan_state();
- }
- if (g_callbacks.get_gwp_asan_metadata) {
- gwp_asan_metadata = g_callbacks.get_gwp_asan_metadata();
- }
+ } else if (g_callbacks.get_process_info) {
+ process_info = g_callbacks.get_process_info();
}
// If sival_int is ~0, it means that the fallback handler has been called
@@ -524,7 +532,7 @@
// This check might be racy if another thread sets NO_NEW_PRIVS, but this should be unlikely,
// you can only set NO_NEW_PRIVS to 1, and the effect should be at worst a single missing
// ANR trace.
- debuggerd_fallback_handler(info, static_cast<ucontext_t*>(context), abort_message);
+ debuggerd_fallback_handler(info, ucontext, process_info.abort_msg);
resend_signal(info);
return;
}
@@ -536,17 +544,14 @@
return;
}
- log_signal_summary(info);
+ log_signal_summary(info, ucontext);
debugger_thread_info thread_info = {
.crashing_tid = __gettid(),
.pseudothread_tid = -1,
.siginfo = info,
.ucontext = context,
- .abort_msg = reinterpret_cast<uintptr_t>(abort_message),
- .fdsan_table = reinterpret_cast<uintptr_t>(android_fdsan_get_fd_table()),
- .gwp_asan_state = reinterpret_cast<uintptr_t>(gwp_asan_state),
- .gwp_asan_metadata = reinterpret_cast<uintptr_t>(gwp_asan_metadata),
+ .process_info = process_info,
};
// Set PR_SET_DUMPABLE to 1, so that crash_dump can ptrace us.
diff --git a/debuggerd/include/debuggerd/handler.h b/debuggerd/include/debuggerd/handler.h
index 4f24360..254ed4f 100644
--- a/debuggerd/include/debuggerd/handler.h
+++ b/debuggerd/include/debuggerd/handler.h
@@ -19,7 +19,9 @@
#include <bionic/reserved_signals.h>
#include <signal.h>
#include <stdint.h>
+#include <string.h>
#include <sys/cdefs.h>
+#include <sys/system_properties.h>
#include <sys/types.h>
__BEGIN_DECLS
@@ -31,13 +33,22 @@
struct AllocationMetadata;
}; // namespace gwp_asan
+// When updating this data structure, CrashInfoDataDynamic and the code in
+// ReadCrashInfo() must also be updated.
+struct debugger_process_info {
+ void* abort_msg;
+ void* fdsan_table;
+ const gwp_asan::AllocatorState* gwp_asan_state;
+ const gwp_asan::AllocationMetadata* gwp_asan_metadata;
+ const char* scudo_stack_depot;
+ const char* scudo_region_info;
+};
+
// These callbacks are called in a signal handler, and thus must be async signal safe.
// If null, the callbacks will not be called.
typedef struct {
- struct abort_msg_t* (*get_abort_message)();
+ debugger_process_info (*get_process_info)();
void (*post_dump)();
- const struct gwp_asan::AllocatorState* (*get_gwp_asan_state)();
- const struct gwp_asan::AllocationMetadata* (*get_gwp_asan_metadata)();
} debuggerd_callbacks_t;
void debuggerd_init(debuggerd_callbacks_t* callbacks);
@@ -50,16 +61,21 @@
#define DEBUGGER_SIGNAL BIONIC_SIGNAL_DEBUGGER
static void __attribute__((__unused__)) debuggerd_register_handlers(struct sigaction* action) {
- sigaction(SIGABRT, action, nullptr);
- sigaction(SIGBUS, action, nullptr);
- sigaction(SIGFPE, action, nullptr);
- sigaction(SIGILL, action, nullptr);
- sigaction(SIGSEGV, action, nullptr);
-#if defined(SIGSTKFLT)
- sigaction(SIGSTKFLT, action, nullptr);
-#endif
- sigaction(SIGSYS, action, nullptr);
- sigaction(SIGTRAP, action, nullptr);
+ char value[PROP_VALUE_MAX] = "";
+ bool enabled =
+ !(__system_property_get("ro.debuggable", value) > 0 && !strcmp(value, "1") &&
+ __system_property_get("debug.debuggerd.disable", value) > 0 && !strcmp(value, "1"));
+ if (enabled) {
+ sigaction(SIGABRT, action, nullptr);
+ sigaction(SIGBUS, action, nullptr);
+ sigaction(SIGFPE, action, nullptr);
+ sigaction(SIGILL, action, nullptr);
+ sigaction(SIGSEGV, action, nullptr);
+ sigaction(SIGSTKFLT, action, nullptr);
+ sigaction(SIGSYS, action, nullptr);
+ sigaction(SIGTRAP, action, nullptr);
+ }
+
sigaction(BIONIC_SIGNAL_DEBUGGER, action, nullptr);
}
diff --git a/debuggerd/libdebuggerd/gwp_asan.cpp b/debuggerd/libdebuggerd/gwp_asan.cpp
index 53df783..f271365 100644
--- a/debuggerd/libdebuggerd/gwp_asan.cpp
+++ b/debuggerd/libdebuggerd/gwp_asan.cpp
@@ -63,12 +63,11 @@
}
GwpAsanCrashData::GwpAsanCrashData(unwindstack::Memory* process_memory,
- uintptr_t gwp_asan_state_ptr, uintptr_t gwp_asan_metadata_ptr,
- const ThreadInfo& thread_info) {
- if (!process_memory || !gwp_asan_metadata_ptr || !gwp_asan_state_ptr) return;
+ const ProcessInfo& process_info, const ThreadInfo& thread_info) {
+ if (!process_memory || !process_info.gwp_asan_metadata || !process_info.gwp_asan_state) return;
// Extract the GWP-ASan regions from the dead process.
- if (!retrieve_gwp_asan_state(process_memory, gwp_asan_state_ptr, &state_)) return;
- metadata_.reset(retrieve_gwp_asan_metadata(process_memory, state_, gwp_asan_metadata_ptr));
+ if (!retrieve_gwp_asan_state(process_memory, process_info.gwp_asan_state, &state_)) return;
+ metadata_.reset(retrieve_gwp_asan_metadata(process_memory, state_, process_info.gwp_asan_metadata));
if (!metadata_.get()) return;
// Get the external crash address from the thread info.
@@ -158,63 +157,6 @@
error_string_, diff, byte_suffix, location_str, alloc_size, alloc_address);
}
-// Build a frame for symbolization using the maps from the provided unwinder.
-// The constructed frame contains just enough information to be used to
-// symbolize a GWP-ASan stack trace.
-static unwindstack::FrameData BuildFrame(unwindstack::Unwinder* unwinder, uintptr_t pc,
- size_t frame_num) {
- unwindstack::FrameData frame;
- frame.num = frame_num;
-
- unwindstack::Maps* maps = unwinder->GetMaps();
- unwindstack::MapInfo* map_info = maps->Find(pc);
- if (!map_info) {
- frame.rel_pc = pc;
- return frame;
- }
-
- unwindstack::Elf* elf =
- map_info->GetElf(unwinder->GetProcessMemory(), unwindstack::Regs::CurrentArch());
-
- uint64_t relative_pc = elf->GetRelPc(pc, map_info);
-
- // Create registers just to get PC adjustment. Doesn't matter what they point
- // to.
- unwindstack::Regs* regs = unwindstack::Regs::CreateFromLocal();
- uint64_t pc_adjustment = regs->GetPcAdjustment(relative_pc, elf);
- relative_pc -= pc_adjustment;
- // The debug PC may be different if the PC comes from the JIT.
- uint64_t debug_pc = relative_pc;
-
- // If we don't have a valid ELF file, check the JIT.
- if (!elf->valid()) {
- unwindstack::JitDebug jit_debug(unwinder->GetProcessMemory());
- uint64_t jit_pc = pc - pc_adjustment;
- unwindstack::Elf* jit_elf = jit_debug.GetElf(maps, jit_pc);
- if (jit_elf != nullptr) {
- debug_pc = jit_pc;
- elf = jit_elf;
- }
- }
-
- // Copy all the things we need into the frame for symbolization.
- frame.rel_pc = relative_pc;
- frame.pc = pc - pc_adjustment;
- frame.map_name = map_info->name;
- frame.map_elf_start_offset = map_info->elf_start_offset;
- frame.map_exact_offset = map_info->offset;
- frame.map_start = map_info->start;
- frame.map_end = map_info->end;
- frame.map_flags = map_info->flags;
- frame.map_load_bias = elf->GetLoadBias();
-
- if (!elf->GetFunctionName(relative_pc, &frame.function_name, &frame.function_offset)) {
- frame.function_name = "";
- frame.function_offset = 0;
- }
- return frame;
-}
-
constexpr size_t kMaxTraceLength = gwp_asan::AllocationMetadata::kMaxTraceLengthToCollect;
bool GwpAsanCrashData::HasDeallocationTrace() const {
@@ -241,7 +183,8 @@
unwinder->SetDisplayBuildID(true);
for (size_t i = 0; i < num_frames; ++i) {
- unwindstack::FrameData frame_data = BuildFrame(unwinder, frames.get()[i], i);
+ unwindstack::FrameData frame_data = unwinder->BuildFrameFromPcOnly(frames.get()[i]);
+ frame_data.num = i;
_LOG(log, logtype::BACKTRACE, " %s\n", unwinder->FormatFrame(frame_data).c_str());
}
}
@@ -267,7 +210,8 @@
unwinder->SetDisplayBuildID(true);
for (size_t i = 0; i < num_frames; ++i) {
- unwindstack::FrameData frame_data = BuildFrame(unwinder, frames.get()[i], i);
+ unwindstack::FrameData frame_data = unwinder->BuildFrameFromPcOnly(frames.get()[i]);
+ frame_data.num = i;
_LOG(log, logtype::BACKTRACE, " %s\n", unwinder->FormatFrame(frame_data).c_str());
}
}
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h b/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h
index aef4c62..6c88733 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h
@@ -38,8 +38,8 @@
// still be responsible, as it terminates when it detects an internal error
// (double free, invalid free). In these cases, we will retrieve the fault
// address from the GWP-ASan allocator's state.
- GwpAsanCrashData(unwindstack::Memory* process_memory, uintptr_t gwp_asan_state_ptr,
- uintptr_t gwp_asan_metadata_ptr, const ThreadInfo& thread_info);
+ GwpAsanCrashData(unwindstack::Memory* process_memory, const ProcessInfo& process_info,
+ const ThreadInfo& thread_info);
// Is GWP-ASan responsible for this crash.
bool CrashIsMine() const;
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h b/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
new file mode 100644
index 0000000..4d00ece
--- /dev/null
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
@@ -0,0 +1,42 @@
+/*
+ * 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 "types.h"
+#include "utility.h"
+
+#include <memory.h>
+
+#include "scudo/interface.h"
+
+class ScudoCrashData {
+ public:
+ ScudoCrashData() = delete;
+ ~ScudoCrashData() = default;
+ ScudoCrashData(unwindstack::Memory* process_memory, const ProcessInfo& process_info);
+
+ bool CrashIsMine() const;
+
+ void DumpCause(log_t* log, unwindstack::Unwinder* unwinder) const;
+
+ private:
+ scudo_error_info error_info_ = {};
+ uintptr_t untagged_fault_addr_;
+
+ void DumpReport(const scudo_error_report* report, log_t* log,
+ unwindstack::Unwinder* unwinder) const;
+};
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
index 291d994..3ff7d62 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
@@ -44,18 +44,13 @@
int open_tombstone(std::string* path);
/* Creates a tombstone file and writes the crash dump to it. */
-void engrave_tombstone(int tombstone_fd, unwindstack::Unwinder* unwinder,
- const OpenFilesList* open_files, pid_t pid, pid_t tid,
- const std::string& process_name, const std::map<pid_t, std::string>& threads,
- uint64_t abort_msg_address, std::string* amfd_data);
+void engrave_tombstone(android::base::unique_fd output_fd, unwindstack::Unwinder* unwinder,
+ const std::map<pid_t, ThreadInfo>& thread_info, pid_t target_thread,
+ const ProcessInfo& process_info, OpenFilesList* open_files,
+ std::string* amfd_data);
void engrave_tombstone_ucontext(int tombstone_fd, uint64_t abort_msg_address, siginfo_t* siginfo,
ucontext_t* ucontext);
-void engrave_tombstone(android::base::unique_fd output_fd, unwindstack::Unwinder* unwinder,
- const std::map<pid_t, ThreadInfo>& thread_info, pid_t target_thread,
- uint64_t abort_msg_address, OpenFilesList* open_files,
- std::string* amfd_data, uintptr_t gwp_asan_state,
- uintptr_t gwp_asan_metadata);
#endif // _DEBUGGERD_TOMBSTONE_H
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/types.h b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
index eb4b1b8..04c4b5c 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/types.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
@@ -35,3 +35,15 @@
int signo = 0;
siginfo_t* siginfo = nullptr;
};
+
+struct ProcessInfo {
+ uintptr_t abort_msg_address = 0;
+ uintptr_t fdsan_table_address = 0;
+ uintptr_t gwp_asan_state = 0;
+ uintptr_t gwp_asan_metadata = 0;
+ uintptr_t scudo_stack_depot = 0;
+ uintptr_t scudo_region_info = 0;
+
+ bool has_fault_address = false;
+ uintptr_t fault_address = 0;
+};
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
index 75bac87..7bfcf5d 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
@@ -93,4 +93,6 @@
const char* get_signame(const siginfo_t*);
const char* get_sigcode(const siginfo_t*);
+uintptr_t get_fault_address(const siginfo_t* siginfo, const ucontext_t* ucontext);
+
#endif // _DEBUGGERD_UTILITY_H
diff --git a/debuggerd/libdebuggerd/scudo.cpp b/debuggerd/libdebuggerd/scudo.cpp
new file mode 100644
index 0000000..f8bfe07
--- /dev/null
+++ b/debuggerd/libdebuggerd/scudo.cpp
@@ -0,0 +1,159 @@
+/*
+ * 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 "libdebuggerd/scudo.h"
+#include "libdebuggerd/gwp_asan.h"
+
+#include "unwindstack/Memory.h"
+#include "unwindstack/Unwinder.h"
+
+#include <bionic/macros.h>
+
+std::unique_ptr<char[]> AllocAndReadFully(unwindstack::Memory* process_memory, uint64_t addr,
+ size_t size) {
+ auto buf = std::make_unique<char[]>(size);
+ if (!process_memory->ReadFully(addr, buf.get(), size)) {
+ return std::unique_ptr<char[]>();
+ }
+ return buf;
+}
+
+static const uintptr_t kTagGranuleSize = 16;
+
+ScudoCrashData::ScudoCrashData(unwindstack::Memory* process_memory,
+ const ProcessInfo& process_info) {
+ if (!process_info.has_fault_address) {
+ return;
+ }
+
+ auto stack_depot = AllocAndReadFully(process_memory, process_info.scudo_stack_depot,
+ __scudo_get_stack_depot_size());
+ auto region_info = AllocAndReadFully(process_memory, process_info.scudo_region_info,
+ __scudo_get_region_info_size());
+
+ untagged_fault_addr_ = untag_address(process_info.fault_address);
+ uintptr_t fault_page = untagged_fault_addr_ & ~(PAGE_SIZE - 1);
+
+ uintptr_t memory_begin = fault_page - PAGE_SIZE * 16;
+ if (memory_begin > fault_page) {
+ return;
+ }
+
+ uintptr_t memory_end = fault_page + PAGE_SIZE * 16;
+ if (memory_end < fault_page) {
+ return;
+ }
+
+ auto memory = std::make_unique<char[]>(memory_end - memory_begin);
+ for (auto i = memory_begin; i != memory_end; i += PAGE_SIZE) {
+ process_memory->ReadFully(i, memory.get() + i - memory_begin, PAGE_SIZE);
+ }
+
+ auto memory_tags = std::make_unique<char[]>((memory_end - memory_begin) / kTagGranuleSize);
+ for (auto i = memory_begin; i != memory_end; i += kTagGranuleSize) {
+ memory_tags[(i - memory_begin) / kTagGranuleSize] = process_memory->ReadTag(i);
+ }
+
+ __scudo_get_error_info(&error_info_, process_info.fault_address, stack_depot.get(),
+ region_info.get(), memory.get(), memory_tags.get(), memory_begin,
+ memory_end - memory_begin);
+}
+
+bool ScudoCrashData::CrashIsMine() const {
+ return error_info_.reports[0].error_type != UNKNOWN;
+}
+
+void ScudoCrashData::DumpCause(log_t* log, unwindstack::Unwinder* unwinder) const {
+ if (error_info_.reports[1].error_type != UNKNOWN) {
+ _LOG(log, logtype::HEADER,
+ "\nNote: multiple potential causes for this crash were detected, listing them in "
+ "decreasing order of probability.\n");
+ }
+
+ size_t report_num = 0;
+ while (report_num < sizeof(error_info_.reports) / sizeof(error_info_.reports[0]) &&
+ error_info_.reports[report_num].error_type != UNKNOWN) {
+ DumpReport(&error_info_.reports[report_num++], log, unwinder);
+ }
+}
+
+void ScudoCrashData::DumpReport(const scudo_error_report* report, log_t* log,
+ unwindstack::Unwinder* unwinder) const {
+ const char *error_type_str;
+ switch (report->error_type) {
+ case USE_AFTER_FREE:
+ error_type_str = "Use After Free";
+ break;
+ case BUFFER_OVERFLOW:
+ error_type_str = "Buffer Overflow";
+ break;
+ case BUFFER_UNDERFLOW:
+ error_type_str = "Buffer Underflow";
+ break;
+ default:
+ error_type_str = "Unknown";
+ break;
+ }
+
+ uintptr_t diff;
+ const char* location_str;
+
+ if (untagged_fault_addr_ < report->allocation_address) {
+ // Buffer Underflow, 6 bytes left of a 41-byte allocation at 0xdeadbeef.
+ location_str = "left of";
+ diff = report->allocation_address - untagged_fault_addr_;
+ } else if (untagged_fault_addr_ - report->allocation_address < report->allocation_size) {
+ // Use After Free, 40 bytes into a 41-byte allocation at 0xdeadbeef.
+ location_str = "into";
+ diff = untagged_fault_addr_ - report->allocation_address;
+ } else {
+ // Buffer Overflow, 6 bytes right of a 41-byte allocation at 0xdeadbeef.
+ location_str = "right of";
+ diff = untagged_fault_addr_ - report->allocation_address - report->allocation_size;
+ }
+
+ // Suffix of 'bytes', i.e. 4 bytes' vs. '1 byte'.
+ const char* byte_suffix = "s";
+ if (diff == 1) {
+ byte_suffix = "";
+ }
+ _LOG(log, logtype::HEADER,
+ "\nCause: [MTE]: %s, %" PRIuPTR " byte%s %s a %zu-byte allocation at 0x%" PRIxPTR "\n",
+ error_type_str, diff, byte_suffix, location_str, report->allocation_size,
+ report->allocation_address);
+
+ if (report->allocation_trace[0]) {
+ _LOG(log, logtype::BACKTRACE, "\nallocated by thread %u:\n", report->allocation_tid);
+ unwinder->SetDisplayBuildID(true);
+ for (size_t i = 0; i < 64 && report->allocation_trace[i]; ++i) {
+ unwindstack::FrameData frame_data =
+ unwinder->BuildFrameFromPcOnly(report->allocation_trace[i]);
+ frame_data.num = i;
+ _LOG(log, logtype::BACKTRACE, " %s\n", unwinder->FormatFrame(frame_data).c_str());
+ }
+ }
+
+ if (report->deallocation_trace[0]) {
+ _LOG(log, logtype::BACKTRACE, "\ndeallocated by thread %u:\n", report->deallocation_tid);
+ unwinder->SetDisplayBuildID(true);
+ for (size_t i = 0; i < 64 && report->deallocation_trace[i]; ++i) {
+ unwindstack::FrameData frame_data =
+ unwinder->BuildFrameFromPcOnly(report->deallocation_trace[i]);
+ frame_data.num = i;
+ _LOG(log, logtype::BACKTRACE, " %s\n", unwinder->FormatFrame(frame_data).c_str());
+ }
+ }
+}
diff --git a/debuggerd/libdebuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp
index eed95bc..aec8c60 100644
--- a/debuggerd/libdebuggerd/test/tombstone_test.cpp
+++ b/debuggerd/libdebuggerd/test/tombstone_test.cpp
@@ -371,7 +371,7 @@
GwpAsanCrashDataTest(
gwp_asan::Error error,
const gwp_asan::AllocationMetadata *responsible_allocation) :
- GwpAsanCrashData(nullptr, 0u, 0u, ThreadInfo{}) {
+ GwpAsanCrashData(nullptr, ProcessInfo{}, ThreadInfo{}) {
is_gwp_asan_responsible_ = true;
error_ = error;
responsible_allocation_ = responsible_allocation;
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index fd52e81..ab65dd1 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>
@@ -55,6 +56,7 @@
#include "libdebuggerd/backtrace.h"
#include "libdebuggerd/gwp_asan.h"
#include "libdebuggerd/open_files_list.h"
+#include "libdebuggerd/scudo.h"
#include "libdebuggerd/utility.h"
#include "gwp_asan/common.h"
@@ -154,16 +156,16 @@
}
static void dump_signal_info(log_t* log, const ThreadInfo& thread_info,
- unwindstack::Memory* process_memory) {
+ const ProcessInfo& process_info, unwindstack::Memory* process_memory) {
char addr_desc[64]; // ", fault addr 0x1234"
- if (signal_has_si_addr(thread_info.siginfo)) {
- void* addr = thread_info.siginfo->si_addr;
+ if (process_info.has_fault_address) {
+ size_t addr = process_info.fault_address;
if (thread_info.siginfo->si_signo == SIGILL) {
uint32_t instruction = {};
- process_memory->Read(reinterpret_cast<uint64_t>(addr), &instruction, sizeof(instruction));
- snprintf(addr_desc, sizeof(addr_desc), "%p (*pc=%#08x)", addr, instruction);
+ process_memory->Read(addr, &instruction, sizeof(instruction));
+ snprintf(addr_desc, sizeof(addr_desc), "0x%zx (*pc=%#08x)", addr, instruction);
} else {
- snprintf(addr_desc, sizeof(addr_desc), "%p", addr);
+ snprintf(addr_desc, sizeof(addr_desc), "0x%zx", addr);
}
} else {
snprintf(addr_desc, sizeof(addr_desc), "--------");
@@ -376,8 +378,7 @@
}
static bool dump_thread(log_t* log, unwindstack::Unwinder* unwinder, const ThreadInfo& thread_info,
- uint64_t abort_msg_address, bool primary_thread,
- const GwpAsanCrashData& gwp_asan_crash_data) {
+ const ProcessInfo& process_info, bool primary_thread) {
log->current_tid = thread_info.tid;
if (!primary_thread) {
_LOG(log, logtype::THREAD, "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n");
@@ -385,18 +386,27 @@
dump_thread_info(log, thread_info);
if (thread_info.siginfo) {
- dump_signal_info(log, thread_info, unwinder->GetProcessMemory().get());
+ dump_signal_info(log, thread_info, process_info, unwinder->GetProcessMemory().get());
}
- if (primary_thread && gwp_asan_crash_data.CrashIsMine()) {
- gwp_asan_crash_data.DumpCause(log);
- } else if (thread_info.siginfo) {
+ std::unique_ptr<GwpAsanCrashData> gwp_asan_crash_data;
+ std::unique_ptr<ScudoCrashData> scudo_crash_data;
+ if (primary_thread) {
+ gwp_asan_crash_data = std::make_unique<GwpAsanCrashData>(unwinder->GetProcessMemory().get(),
+ process_info, thread_info);
+ scudo_crash_data =
+ std::make_unique<ScudoCrashData>(unwinder->GetProcessMemory().get(), process_info);
+ }
+
+ if (primary_thread && gwp_asan_crash_data->CrashIsMine()) {
+ gwp_asan_crash_data->DumpCause(log);
+ } else if (thread_info.siginfo && !(primary_thread && scudo_crash_data->CrashIsMine())) {
dump_probable_cause(log, thread_info.siginfo, unwinder->GetMaps(),
thread_info.registers.get());
}
if (primary_thread) {
- dump_abort_message(log, unwinder->GetProcessMemory().get(), abort_msg_address);
+ dump_abort_message(log, unwinder->GetProcessMemory().get(), process_info.abort_msg_address);
}
dump_registers(log, thread_info.registers.get());
@@ -413,14 +423,16 @@
}
if (primary_thread) {
- if (gwp_asan_crash_data.HasDeallocationTrace()) {
- gwp_asan_crash_data.DumpDeallocationTrace(log, unwinder);
+ if (gwp_asan_crash_data->HasDeallocationTrace()) {
+ gwp_asan_crash_data->DumpDeallocationTrace(log, unwinder);
}
- if (gwp_asan_crash_data.HasAllocationTrace()) {
- gwp_asan_crash_data.DumpAllocationTrace(log, unwinder);
+ if (gwp_asan_crash_data->HasAllocationTrace()) {
+ gwp_asan_crash_data->DumpAllocationTrace(log, unwinder);
}
+ scudo_crash_data->DumpCause(log, unwinder);
+
unwindstack::Maps* maps = unwinder->GetMaps();
dump_memory_and_code(log, maps, unwinder->GetProcessMemory().get(),
thread_info.registers.get());
@@ -442,8 +454,6 @@
// that don't match the specified pid, and writes them to the tombstone file.
//
// If "tail" is non-zero, log the last "tail" number of lines.
-static EventTagMap* g_eventTagMap = NULL;
-
static void dump_log_file(log_t* log, pid_t pid, const char* filename, unsigned int tail) {
bool first = true;
logger_list* logger_list;
@@ -452,8 +462,8 @@
return;
}
- logger_list = android_logger_list_open(
- android_name_to_log_id(filename), ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, tail, pid);
+ logger_list =
+ android_logger_list_open(android_name_to_log_id(filename), ANDROID_LOG_NONBLOCK, tail, pid);
if (!logger_list) {
ALOGE("Unable to open %s: %s\n", filename, strerror(errno));
@@ -502,21 +512,6 @@
ptm = localtime_r(&sec, &tmBuf);
strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm);
- if (log_entry.id() == LOG_ID_EVENTS) {
- if (!g_eventTagMap) {
- g_eventTagMap = android_openEventTagMap(nullptr);
- }
- AndroidLogEntry e;
- char buf[512];
- if (android_log_processBinaryLogBuffer(&log_entry.entry, &e, g_eventTagMap, buf,
- sizeof(buf)) == 0) {
- _LOG(log, logtype::LOGS, "%s.%03d %5d %5d %c %-8.*s: %s\n", timeBuf,
- log_entry.entry.nsec / 1000000, log_entry.entry.pid, log_entry.entry.tid, 'I',
- (int)e.tagLen, e.tag, e.message);
- }
- continue;
- }
-
char* msg = log_entry.msg();
if (msg == nullptr) {
continue;
@@ -601,15 +596,16 @@
LOG(FATAL) << "Failed to init unwinder object.";
}
- engrave_tombstone(unique_fd(dup(tombstone_fd)), &unwinder, threads, tid, abort_msg_address,
- nullptr, nullptr, 0u, 0u);
+ ProcessInfo process_info;
+ process_info.abort_msg_address = abort_msg_address;
+ engrave_tombstone(unique_fd(dup(tombstone_fd)), &unwinder, threads, tid, process_info, nullptr,
+ nullptr);
}
void engrave_tombstone(unique_fd output_fd, unwindstack::Unwinder* unwinder,
const std::map<pid_t, ThreadInfo>& threads, pid_t target_thread,
- uint64_t abort_msg_address, OpenFilesList* open_files,
- std::string* amfd_data, uintptr_t gwp_asan_state_ptr,
- uintptr_t gwp_asan_metadata_ptr) {
+ const ProcessInfo& process_info, OpenFilesList* open_files,
+ std::string* amfd_data) {
// don't copy log messages to tombstone unless this is a dev device
bool want_logs = android::base::GetBoolProperty("ro.debuggable", false);
@@ -628,12 +624,7 @@
LOG(FATAL) << "failed to find target thread";
}
- GwpAsanCrashData gwp_asan_crash_data(unwinder->GetProcessMemory().get(),
- gwp_asan_state_ptr,
- gwp_asan_metadata_ptr, it->second);
-
- dump_thread(&log, unwinder, it->second, abort_msg_address, true,
- gwp_asan_crash_data);
+ dump_thread(&log, unwinder, it->second, process_info, true);
if (want_logs) {
dump_logs(&log, it->second.pid, 50);
@@ -644,7 +635,7 @@
continue;
}
- dump_thread(&log, unwinder, thread_info, 0, false, gwp_asan_crash_data);
+ dump_thread(&log, unwinder, thread_info, process_info, false);
}
if (open_files) {
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index 0a1d2a4..c8a3431 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -35,6 +35,7 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
+#include <bionic/mte_kernel.h>
#include <bionic/reserved_signals.h>
#include <debuggerd/handler.h>
#include <log/log.h>
@@ -374,6 +375,12 @@
return "SEGV_ADIDERR";
case SEGV_ADIPERR:
return "SEGV_ADIPERR";
+#if defined(ANDROID_EXPERIMENTAL_MTE)
+ case SEGV_MTEAERR:
+ return "SEGV_MTEAERR";
+ case SEGV_MTESERR:
+ return "SEGV_MTESERR";
+#endif
}
static_assert(NSIGSEGV == SEGV_ADIPERR, "missing SEGV_* si_code");
break;
@@ -449,3 +456,40 @@
_LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, unwinder->FormatFrame(i).c_str());
}
}
+
+#if defined(__aarch64__)
+#define FAR_MAGIC 0x46415201
+
+struct far_context {
+ struct _aarch64_ctx head;
+ __u64 far;
+};
+#endif
+
+uintptr_t get_fault_address(const siginfo_t* siginfo, const ucontext_t* ucontext) {
+ (void)ucontext;
+#if defined(__aarch64__)
+ // This relies on a kernel patch:
+ // https://patchwork.kernel.org/patch/11435077/
+ // that hasn't been accepted into the kernel yet. TODO(pcc): Update this to
+ // use the official interface once it lands.
+ auto* begin = reinterpret_cast<const char*>(ucontext->uc_mcontext.__reserved);
+ auto* end = begin + sizeof(ucontext->uc_mcontext.__reserved);
+ auto* ptr = begin;
+ while (1) {
+ auto* ctx = reinterpret_cast<const _aarch64_ctx*>(ptr);
+ if (ctx->magic == 0) {
+ break;
+ }
+ if (ctx->magic == FAR_MAGIC) {
+ auto* far_ctx = reinterpret_cast<const far_context*>(ctx);
+ return far_ctx->far;
+ }
+ ptr += ctx->size;
+ if (ctx->size % sizeof(void*) != 0 || ptr < begin || ptr >= end) {
+ break;
+ }
+ }
+#endif
+ return reinterpret_cast<uintptr_t>(siginfo->si_addr);
+}
diff --git a/debuggerd/protocol.h b/debuggerd/protocol.h
index bf53864..53a76ea 100644
--- a/debuggerd/protocol.h
+++ b/debuggerd/protocol.h
@@ -85,26 +85,24 @@
uint32_t version;
};
-struct __attribute__((__packed__)) CrashInfoDataV1 {
+struct __attribute__((__packed__)) CrashInfoDataStatic {
siginfo_t siginfo;
ucontext_t ucontext;
uintptr_t abort_msg_address;
};
-struct __attribute__((__packed__)) CrashInfoDataV2 : public CrashInfoDataV1 {
+struct __attribute__((__packed__)) CrashInfoDataDynamic : public CrashInfoDataStatic {
uintptr_t fdsan_table_address;
-};
-
-struct __attribute__((__packed__)) CrashInfoDataV3 : public CrashInfoDataV2 {
uintptr_t gwp_asan_state;
uintptr_t gwp_asan_metadata;
+ uintptr_t scudo_stack_depot;
+ uintptr_t scudo_region_info;
};
struct __attribute__((__packed__)) CrashInfo {
CrashInfoHeader header;
union {
- CrashInfoDataV1 v1;
- CrashInfoDataV2 v2;
- CrashInfoDataV3 v3;
+ CrashInfoDataStatic s;
+ CrashInfoDataDynamic d;
} data;
};
diff --git a/diagnose_usb/Android.bp b/diagnose_usb/Android.bp
index 6bee28c..93d13bd 100644
--- a/diagnose_usb/Android.bp
+++ b/diagnose_usb/Android.bp
@@ -3,6 +3,11 @@
cflags: ["-Wall", "-Wextra", "-Werror"],
host_supported: true,
recovery_available: true,
+ apex_available: [
+ "com.android.adbd",
+ // TODO(b/151398197) remove the below
+ "//apex_available:platform",
+ ],
target: {
windows: {
enabled: true,
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index a757d56..bdb786c 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -115,9 +115,12 @@
"device/fastboot_device.cpp",
"device/flashing.cpp",
"device/main.cpp",
+ "device/usb.cpp",
"device/usb_client.cpp",
+ "device/tcp_client.cpp",
"device/utility.cpp",
"device/variables.cpp",
+ "socket.cpp",
],
shared_libs: [
@@ -125,7 +128,6 @@
"android.hardware.boot@1.1",
"android.hardware.fastboot@1.0",
"android.hardware.health@2.0",
- "libadbd",
"libasyncio",
"libbase",
"libbootloader_message",
@@ -143,11 +145,14 @@
],
static_libs: [
+ "libgtest_prod",
"libhealthhalutils",
"libsnapshot_nobinder",
+ "update_metadata-protos",
],
header_libs: [
+ "avb_headers",
"libsnapshot_headers",
]
}
diff --git a/fastboot/bootimg_utils.cpp b/fastboot/bootimg_utils.cpp
index 46d4bd3..2c0989e 100644
--- a/fastboot/bootimg_utils.cpp
+++ b/fastboot/bootimg_utils.cpp
@@ -34,14 +34,54 @@
#include <stdlib.h>
#include <string.h>
-void bootimg_set_cmdline(boot_img_hdr_v2* h, const std::string& cmdline) {
+static void bootimg_set_cmdline_v3(boot_img_hdr_v3* h, const std::string& cmdline) {
if (cmdline.size() >= sizeof(h->cmdline)) die("command line too large: %zu", cmdline.size());
strcpy(reinterpret_cast<char*>(h->cmdline), cmdline.c_str());
}
+void bootimg_set_cmdline(boot_img_hdr_v2* h, const std::string& cmdline) {
+ if (h->header_version == 3) {
+ return bootimg_set_cmdline_v3(reinterpret_cast<boot_img_hdr_v3*>(h), cmdline);
+ }
+ if (cmdline.size() >= sizeof(h->cmdline)) die("command line too large: %zu", cmdline.size());
+ strcpy(reinterpret_cast<char*>(h->cmdline), cmdline.c_str());
+}
+
+static boot_img_hdr_v3* mkbootimg_v3(const std::vector<char>& kernel,
+ const std::vector<char>& ramdisk, const boot_img_hdr_v2& src,
+ std::vector<char>* out) {
+#define V3_PAGE_SIZE 4096
+ const size_t page_mask = V3_PAGE_SIZE - 1;
+ int64_t kernel_actual = (kernel.size() + page_mask) & (~page_mask);
+ int64_t ramdisk_actual = (ramdisk.size() + page_mask) & (~page_mask);
+
+ int64_t bootimg_size = V3_PAGE_SIZE + kernel_actual + ramdisk_actual;
+ out->resize(bootimg_size);
+
+ boot_img_hdr_v3* hdr = reinterpret_cast<boot_img_hdr_v3*>(out->data());
+
+ memcpy(hdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE);
+ hdr->kernel_size = kernel.size();
+ hdr->ramdisk_size = ramdisk.size();
+ hdr->os_version = src.os_version;
+ hdr->header_size = sizeof(boot_img_hdr_v3);
+ hdr->header_version = 3;
+
+ memcpy(hdr->magic + V3_PAGE_SIZE, kernel.data(), kernel.size());
+ memcpy(hdr->magic + V3_PAGE_SIZE + kernel_actual, ramdisk.data(), ramdisk.size());
+
+ return hdr;
+}
+
boot_img_hdr_v2* mkbootimg(const std::vector<char>& kernel, const std::vector<char>& ramdisk,
const std::vector<char>& second, const std::vector<char>& dtb,
size_t base, const boot_img_hdr_v2& src, std::vector<char>* out) {
+ if (src.header_version == 3) {
+ if (!second.empty() || !dtb.empty()) {
+ die("Second stage bootloader and dtb not supported in v3 boot image\n");
+ }
+ return reinterpret_cast<boot_img_hdr_v2*>(mkbootimg_v3(kernel, ramdisk, src, out));
+ }
const size_t page_mask = src.page_size - 1;
int64_t header_actual = (sizeof(boot_img_hdr_v1) + page_mask) & (~page_mask);
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index ca120c6..2553353 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -31,6 +31,7 @@
#include <cutils/android_reboot.h>
#include <ext4_utils/wipe.h>
#include <fs_mgr.h>
+#include <fs_mgr/roots.h>
#include <libgsi/libgsi.h>
#include <liblp/builder.h>
#include <liblp/liblp.h>
@@ -624,7 +625,7 @@
if (!sm) {
return device->WriteFail("Unable to create SnapshotManager");
}
- if (!sm->HandleImminentDataWipe()) {
+ if (!sm->FinishMergeInRecovery()) {
return device->WriteFail("Unable to finish snapshot merge");
}
} else {
diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp
index 31fc359..1b0859f 100644
--- a/fastboot/device/fastboot_device.cpp
+++ b/fastboot/device/fastboot_device.cpp
@@ -16,18 +16,24 @@
#include "fastboot_device.h"
+#include <algorithm>
+
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <android-base/strings.h>
#include <android/hardware/boot/1.0/IBootControl.h>
#include <android/hardware/fastboot/1.0/IFastboot.h>
+#include <fs_mgr.h>
+#include <fs_mgr/roots.h>
#include <healthhalutils/HealthHalUtils.h>
-#include <algorithm>
-
#include "constants.h"
#include "flashing.h"
+#include "tcp_client.h"
#include "usb_client.h"
+using android::fs_mgr::EnsurePathUnmounted;
+using android::fs_mgr::Fstab;
using ::android::hardware::hidl_string;
using ::android::hardware::boot::V1_0::IBootControl;
using ::android::hardware::boot::V1_0::Slot;
@@ -56,14 +62,26 @@
{FB_CMD_GSI, GsiHandler},
{FB_CMD_SNAPSHOT_UPDATE, SnapshotUpdateHandler},
}),
- transport_(std::make_unique<ClientUsbTransport>()),
boot_control_hal_(IBootControl::getService()),
health_hal_(get_health_service()),
fastboot_hal_(IFastboot::getService()),
active_slot_("") {
+ if (android::base::GetProperty("fastbootd.protocol", "usb") == "tcp") {
+ transport_ = std::make_unique<ClientTcpTransport>();
+ } else {
+ transport_ = std::make_unique<ClientUsbTransport>();
+ }
+
if (boot_control_hal_) {
boot1_1_ = android::hardware::boot::V1_1::IBootControl::castFrom(boot_control_hal_);
}
+
+ // Make sure cache is unmounted, since recovery will have mounted it for
+ // logging.
+ Fstab fstab;
+ if (ReadDefaultFstab(&fstab)) {
+ EnsurePathUnmounted(&fstab, "/cache");
+ }
}
FastbootDevice::~FastbootDevice() {
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index 7e7e507..1bf4c9c 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -31,6 +31,7 @@
#include <ext4_utils/ext4_utils.h>
#include <fs_mgr_overlayfs.h>
#include <fstab/fstab.h>
+#include <libavb/libavb.h>
#include <liblp/builder.h>
#include <liblp/liblp.h>
#include <libsnapshot/snapshot.h>
@@ -122,6 +123,27 @@
}
}
+static void CopyAVBFooter(std::vector<char>* data, const uint64_t block_device_size) {
+ if (data->size() < AVB_FOOTER_SIZE) {
+ return;
+ }
+ std::string footer;
+ uint64_t footer_offset = data->size() - AVB_FOOTER_SIZE;
+ for (int idx = 0; idx < AVB_FOOTER_MAGIC_LEN; idx++) {
+ footer.push_back(data->at(footer_offset + idx));
+ }
+ if (0 != footer.compare(AVB_FOOTER_MAGIC)) {
+ return;
+ }
+
+ // copy AVB footer from end of data to end of block device
+ uint64_t original_data_size = data->size();
+ data->resize(block_device_size, 0);
+ for (int idx = 0; idx < AVB_FOOTER_SIZE; idx++) {
+ data->at(block_device_size - 1 - idx) = data->at(original_data_size - 1 - idx);
+ }
+}
+
int Flash(FastbootDevice* device, const std::string& partition_name) {
PartitionHandle handle;
if (!OpenPartition(device, partition_name, &handle)) {
@@ -131,11 +153,19 @@
std::vector<char> data = std::move(device->download_data());
if (data.size() == 0) {
return -EINVAL;
- } else if (data.size() > get_block_device_size(handle.fd())) {
+ }
+ uint64_t block_device_size = get_block_device_size(handle.fd());
+ if (data.size() > block_device_size) {
return -EOVERFLOW;
+ } else if (data.size() < block_device_size &&
+ (partition_name == "boot" || partition_name == "boot_a" ||
+ partition_name == "boot_b")) {
+ CopyAVBFooter(&data, block_device_size);
}
WipeOverlayfsForPartition(device, partition_name);
- return FlashBlockDevice(handle.fd(), data);
+ int result = FlashBlockDevice(handle.fd(), data);
+ sync();
+ return result;
}
bool UpdateSuper(FastbootDevice* device, const std::string& super_name, bool wipe) {
@@ -165,6 +195,7 @@
return device->WriteFail("Unable to flash new partition table");
}
fs_mgr_overlayfs_teardown();
+ sync();
return device->WriteOkay("Successfully flashed partition table");
}
@@ -204,5 +235,6 @@
return device->WriteFail("Unable to write new partition table");
}
fs_mgr_overlayfs_teardown();
+ sync();
return device->WriteOkay("Successfully updated partition table");
}
diff --git a/fastboot/device/tcp_client.cpp b/fastboot/device/tcp_client.cpp
new file mode 100644
index 0000000..ec5e1e3
--- /dev/null
+++ b/fastboot/device/tcp_client.cpp
@@ -0,0 +1,176 @@
+/*
+ * 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 "tcp_client.h"
+#include "constants.h"
+
+#include <android-base/errors.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+static constexpr int kDefaultPort = 5554;
+static constexpr int kProtocolVersion = 1;
+static constexpr int kHandshakeTimeoutMs = 2000;
+static constexpr size_t kHandshakeLength = 4;
+
+// Extract the big-endian 8-byte message length into a 64-bit number.
+static uint64_t ExtractMessageLength(const void* buffer) {
+ uint64_t ret = 0;
+ for (int i = 0; i < 8; ++i) {
+ ret |= uint64_t{reinterpret_cast<const uint8_t*>(buffer)[i]} << (56 - i * 8);
+ }
+ return ret;
+}
+
+// Encode the 64-bit number into a big-endian 8-byte message length.
+static void EncodeMessageLength(uint64_t length, void* buffer) {
+ for (int i = 0; i < 8; ++i) {
+ reinterpret_cast<uint8_t*>(buffer)[i] = length >> (56 - i * 8);
+ }
+}
+
+ClientTcpTransport::ClientTcpTransport() {
+ service_ = Socket::NewServer(Socket::Protocol::kTcp, kDefaultPort);
+
+ // A workaround to notify recovery to continue its work.
+ android::base::SetProperty("sys.usb.ffs.ready", "1");
+}
+
+ssize_t ClientTcpTransport::Read(void* data, size_t len) {
+ if (len > SSIZE_MAX) {
+ return -1;
+ }
+
+ size_t total_read = 0;
+ do {
+ // Read a new message
+ while (message_bytes_left_ == 0) {
+ if (socket_ == nullptr) {
+ ListenFastbootSocket();
+ }
+
+ char buffer[8];
+ if (socket_->ReceiveAll(buffer, 8, 0) == 8) {
+ message_bytes_left_ = ExtractMessageLength(buffer);
+ } else {
+ // If connection is closed by host, Receive will return 0 immediately.
+ socket_.reset(nullptr);
+ // In DATA phase, return error.
+ if (downloading_) {
+ return -1;
+ }
+ }
+ }
+
+ size_t read_length = len - total_read;
+ if (read_length > message_bytes_left_) {
+ read_length = message_bytes_left_;
+ }
+ ssize_t bytes_read =
+ socket_->ReceiveAll(reinterpret_cast<char*>(data) + total_read, read_length, 0);
+ if (bytes_read == -1) {
+ socket_.reset(nullptr);
+ return -1;
+ } else {
+ message_bytes_left_ -= bytes_read;
+ total_read += bytes_read;
+ }
+ // There are more than one DATA phases if the downloading buffer is too
+ // large, like a very big system image. All of data phases should be
+ // received until the whole buffer is filled in that case.
+ } while (downloading_ && total_read < len);
+
+ return total_read;
+}
+
+ssize_t ClientTcpTransport::Write(const void* data, size_t len) {
+ if (socket_ == nullptr || len > SSIZE_MAX) {
+ return -1;
+ }
+
+ // Use multi-buffer writes for better performance.
+ char header[8];
+ EncodeMessageLength(len, header);
+
+ if (!socket_->Send(std::vector<cutils_socket_buffer_t>{{header, 8}, {data, len}})) {
+ socket_.reset(nullptr);
+ return -1;
+ }
+
+ // In DATA phase
+ if (android::base::StartsWith(reinterpret_cast<const char*>(data), RESPONSE_DATA)) {
+ downloading_ = true;
+ } else {
+ downloading_ = false;
+ }
+
+ return len;
+}
+
+int ClientTcpTransport::Close() {
+ if (socket_ == nullptr) {
+ return -1;
+ }
+ socket_.reset(nullptr);
+
+ return 0;
+}
+
+int ClientTcpTransport::Reset() {
+ return Close();
+}
+
+void ClientTcpTransport::ListenFastbootSocket() {
+ while (true) {
+ socket_ = service_->Accept();
+
+ // Handshake
+ char buffer[kHandshakeLength + 1];
+ buffer[kHandshakeLength] = '\0';
+ if (socket_->ReceiveAll(buffer, kHandshakeLength, kHandshakeTimeoutMs) !=
+ kHandshakeLength) {
+ PLOG(ERROR) << "No Handshake message received";
+ socket_.reset(nullptr);
+ continue;
+ }
+
+ if (memcmp(buffer, "FB", 2) != 0) {
+ PLOG(ERROR) << "Unrecognized initialization message";
+ socket_.reset(nullptr);
+ continue;
+ }
+
+ int version = 0;
+ if (!android::base::ParseInt(buffer + 2, &version) || version < kProtocolVersion) {
+ LOG(ERROR) << "Unknown TCP protocol version " << buffer + 2
+ << ", our version: " << kProtocolVersion;
+ socket_.reset(nullptr);
+ continue;
+ }
+
+ std::string handshake_message(android::base::StringPrintf("FB%02d", kProtocolVersion));
+ if (!socket_->Send(handshake_message.c_str(), kHandshakeLength)) {
+ PLOG(ERROR) << "Failed to send initialization message";
+ socket_.reset(nullptr);
+ continue;
+ }
+
+ break;
+ }
+}
diff --git a/fastboot/device/tcp_client.h b/fastboot/device/tcp_client.h
new file mode 100644
index 0000000..32e9834
--- /dev/null
+++ b/fastboot/device/tcp_client.h
@@ -0,0 +1,42 @@
+/*
+ * 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 <memory>
+
+#include "socket.h"
+#include "transport.h"
+
+class ClientTcpTransport : public Transport {
+ public:
+ ClientTcpTransport();
+ ~ClientTcpTransport() override = default;
+
+ ssize_t Read(void* data, size_t len) override;
+ ssize_t Write(const void* data, size_t len) override;
+ int Close() override;
+ int Reset() override;
+
+ private:
+ void ListenFastbootSocket();
+
+ std::unique_ptr<Socket> service_;
+ std::unique_ptr<Socket> socket_;
+ uint64_t message_bytes_left_ = 0;
+ bool downloading_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(ClientTcpTransport);
+};
diff --git a/adb/daemon/usb_legacy.cpp b/fastboot/device/usb.cpp
similarity index 70%
rename from adb/daemon/usb_legacy.cpp
rename to fastboot/device/usb.cpp
index fe80e7d..4bee7b2 100644
--- a/adb/daemon/usb_legacy.cpp
+++ b/fastboot/device/usb.cpp
@@ -14,9 +14,7 @@
* limitations under the License.
*/
-#define TRACE_TAG USB
-
-#include "sysdeps.h"
+#include "usb.h"
#include <dirent.h>
#include <errno.h>
@@ -41,12 +39,9 @@
#include <android-base/logging.h>
#include <android-base/properties.h>
-#include "adb.h"
-#include "adbd/usb.h"
-#include "transport.h"
-
using namespace std::chrono_literals;
+#define D(...)
#define MAX_PACKET_SIZE_FS 64
#define MAX_PACKET_SIZE_HS 512
#define MAX_PACKET_SIZE_SS 1024
@@ -56,8 +51,6 @@
// Number of buffers needed to fit MAX_PAYLOAD, with an extra for ZLPs.
#define USB_FFS_NUM_BUFS ((4 * MAX_PAYLOAD / USB_FFS_BULK_SIZE) + 1)
-static unique_fd& dummy_fd = *new unique_fd();
-
static void aio_block_init(aio_block* aiob, unsigned num_bufs) {
aiob->iocb.resize(num_bufs);
aiob->iocbs.resize(num_bufs);
@@ -82,46 +75,6 @@
}
}
-static bool init_functionfs(struct usb_handle* h) {
- LOG(INFO) << "initializing functionfs";
- if (!open_functionfs(&h->control, &h->bulk_out, &h->bulk_in)) {
- return false;
- }
-
- h->read_aiob.fd = h->bulk_out.get();
- h->write_aiob.fd = h->bulk_in.get();
- h->reads_zero_packets = true;
- return true;
-}
-
-static void usb_legacy_ffs_open_thread(usb_handle* usb) {
- adb_thread_setname("usb legacy ffs open");
-
- while (true) {
- // wait until the USB device needs opening
- std::unique_lock<std::mutex> lock(usb->lock);
- while (!usb->open_new_connection) {
- usb->notify.wait(lock);
- }
- usb->open_new_connection = false;
- lock.unlock();
-
- while (true) {
- if (init_functionfs(usb)) {
- LOG(INFO) << "functionfs successfully initialized";
- break;
- }
- std::this_thread::sleep_for(1s);
- }
-
- LOG(INFO) << "registering usb transport";
- register_usb_transport(usb, nullptr, nullptr, 1);
- }
-
- // never gets here
- abort();
-}
-
static int usb_ffs_write(usb_handle* h, const void* data, int len) {
D("about to write (fd=%d, len=%d)", h->bulk_in.get(), len);
@@ -129,7 +82,7 @@
int orig_len = len;
while (len > 0) {
int write_len = std::min(USB_FFS_BULK_SIZE, len);
- int n = adb_write(h->bulk_in, buf, write_len);
+ int n = write(h->bulk_in, buf, write_len);
if (n < 0) {
D("ERROR: fd = %d, n = %d: %s", h->bulk_in.get(), n, strerror(errno));
return -1;
@@ -150,7 +103,7 @@
unsigned count = 0;
while (len > 0) {
int read_len = std::min(USB_FFS_BULK_SIZE, len);
- int n = adb_read(h->bulk_out, buf, read_len);
+ int n = read(h->bulk_out, buf, read_len);
if (n < 0) {
D("ERROR: fd = %d, n = %d: %s", h->bulk_out.get(), n, strerror(errno));
return -1;
@@ -232,7 +185,7 @@
}
}
-static int usb_ffs_aio_read(usb_handle* h, void* data, int len, bool allow_partial) {
+static int usb_ffs_aio_read(usb_handle* h, void* data, int len, bool /* allow_partial */) {
return usb_ffs_do_aio(h, data, len, true);
}
@@ -240,32 +193,9 @@
return usb_ffs_do_aio(h, data, len, false);
}
-static void usb_ffs_kick(usb_handle* h) {
- int err;
-
- err = ioctl(h->bulk_in.get(), FUNCTIONFS_CLEAR_HALT);
- if (err < 0) {
- D("[ kick: source (fd=%d) clear halt failed (%d) ]", h->bulk_in.get(), errno);
- }
-
- err = ioctl(h->bulk_out.get(), FUNCTIONFS_CLEAR_HALT);
- if (err < 0) {
- D("[ kick: sink (fd=%d) clear halt failed (%d) ]", h->bulk_out.get(), errno);
- }
-
- // don't close ep0 here, since we may not need to reinitialize it with
- // the same descriptors again. if however ep1/ep2 fail to re-open in
- // init_functionfs, only then would we close and open ep0 again.
- // Ditto the comment in usb_adb_kick.
- h->kicked = true;
- TEMP_FAILURE_RETRY(dup2(dummy_fd.get(), h->bulk_out.get()));
- TEMP_FAILURE_RETRY(dup2(dummy_fd.get(), h->bulk_in.get()));
-}
-
static void usb_ffs_close(usb_handle* h) {
LOG(INFO) << "closing functionfs transport";
- h->kicked = false;
h->bulk_out.reset();
h->bulk_in.reset();
@@ -291,37 +221,6 @@
aio_block_init(&h->write_aiob, num_bufs);
}
h->io_size = io_size;
- h->kick = usb_ffs_kick;
h->close = usb_ffs_close;
return h;
}
-
-void usb_init_legacy() {
- D("[ usb_init - using legacy FunctionFS ]");
- dummy_fd.reset(adb_open("/dev/null", O_WRONLY | O_CLOEXEC));
- CHECK_NE(-1, dummy_fd.get());
-
- std::thread(usb_legacy_ffs_open_thread, create_usb_handle(USB_FFS_NUM_BUFS, USB_FFS_BULK_SIZE))
- .detach();
-}
-
-int usb_write(usb_handle* h, const void* data, int len) {
- return h->write(h, data, len);
-}
-
-int usb_read(usb_handle* h, void* data, int len) {
- return h->read(h, data, len, false /* allow_partial */);
-}
-
-int usb_close(usb_handle* h) {
- h->close(h);
- return 0;
-}
-
-void usb_reset(usb_handle* h) {
- usb_close(h);
-}
-
-void usb_kick(usb_handle* h) {
- h->kick(h);
-}
diff --git a/adb/daemon/include/adbd/usb.h b/fastboot/device/usb.h
similarity index 84%
rename from adb/daemon/include/adbd/usb.h
rename to fastboot/device/usb.h
index 2204246..6c3f542 100644
--- a/adb/daemon/include/adbd/usb.h
+++ b/fastboot/device/usb.h
@@ -36,17 +36,14 @@
};
struct usb_handle {
- usb_handle() : kicked(false) {
- }
+ usb_handle() {}
std::condition_variable notify;
std::mutex lock;
- std::atomic<bool> kicked;
bool open_new_connection = true;
int (*write)(usb_handle* h, const void* data, int len);
int (*read)(usb_handle* h, void* data, int len, bool allow_partial);
- void (*kick)(usb_handle* h);
void (*close)(usb_handle* h);
// FunctionFS
@@ -63,6 +60,4 @@
size_t io_size;
};
-usb_handle *create_usb_handle(unsigned num_bufs, unsigned io_size);
-bool open_functionfs(android::base::unique_fd* control, android::base::unique_fd* bulk_out,
- android::base::unique_fd* bulk_in);
+usb_handle* create_usb_handle(unsigned num_bufs, unsigned io_size);
diff --git a/fastboot/device/usb_client.cpp b/fastboot/device/usb_client.cpp
index 9c80765..c653167 100644
--- a/fastboot/device/usb_client.cpp
+++ b/fastboot/device/usb_client.cpp
@@ -146,7 +146,7 @@
},
};
-#define STR_INTERFACE_ "fastboot"
+#define STR_INTERFACE_ "fastbootd"
static const struct {
struct usb_functionfs_strings_head header;
diff --git a/fastboot/device/usb_client.h b/fastboot/device/usb_client.h
index e6a1a8b..e702a0d 100644
--- a/fastboot/device/usb_client.h
+++ b/fastboot/device/usb_client.h
@@ -17,7 +17,7 @@
#include <memory>
-#include <adbd/usb.h>
+#include "usb.h"
#include "transport.h"
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 7fdc28b..46d1d36 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -258,6 +258,10 @@
static int list_devices_callback(usb_ifc_info* info) {
if (match_fastboot_with_serial(info, nullptr) == 0) {
std::string serial = info->serial_number;
+ std::string interface = info->interface;
+ if (interface.empty()) {
+ interface = "fastboot";
+ }
if (!info->writable) {
serial = UsbNoPermissionsShortHelpText();
}
@@ -266,9 +270,9 @@
}
// output compatible with "adb devices"
if (!g_long_listing) {
- printf("%s\tfastboot", serial.c_str());
+ printf("%s\t%s", serial.c_str(), interface.c_str());
} else {
- printf("%-22s fastboot", serial.c_str());
+ printf("%-22s %s", serial.c_str(), interface.c_str());
if (strlen(info->device_path) > 0) printf(" %s", info->device_path);
}
putchar('\n');
@@ -464,7 +468,7 @@
}
// Is this actually a boot image?
- if (kernel_data.size() < sizeof(boot_img_hdr_v2)) {
+ if (kernel_data.size() < sizeof(boot_img_hdr_v3)) {
die("cannot load '%s': too short", kernel.c_str());
}
if (!memcmp(kernel_data.data(), BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
@@ -493,7 +497,7 @@
std::vector<char> dtb_data;
if (!g_dtb_path.empty()) {
- if (g_boot_img_hdr.header_version < 2) {
+ if (g_boot_img_hdr.header_version != 2) {
die("Argument dtb not supported for boot image header version %d\n",
g_boot_img_hdr.header_version);
}
@@ -1225,7 +1229,7 @@
static void CancelSnapshotIfNeeded() {
std::string merge_status = "none";
if (fb->GetVar(FB_VAR_SNAPSHOT_UPDATE_STATUS, &merge_status) == fastboot::SUCCESS &&
- merge_status != "none") {
+ !merge_status.empty() && merge_status != "none") {
fb->SnapshotUpdateCommand("cancel");
}
}
diff --git a/fastboot/fuzzy_fastboot/Android.bp b/fastboot/fuzzy_fastboot/Android.bp
index bb54fd9..aa449b2 100644
--- a/fastboot/fuzzy_fastboot/Android.bp
+++ b/fastboot/fuzzy_fastboot/Android.bp
@@ -49,6 +49,6 @@
auto_gen_config: false,
test_suites: [
"general-tests",
- "vts-core",
+ "vts",
],
}
diff --git a/fastboot/fuzzy_fastboot/main.cpp b/fastboot/fuzzy_fastboot/main.cpp
index d9167e7..e7f785b 100644
--- a/fastboot/fuzzy_fastboot/main.cpp
+++ b/fastboot/fuzzy_fastboot/main.cpp
@@ -261,6 +261,10 @@
GTEST_LOG_(INFO) << "Flashing a logical partition..";
EXPECT_EQ(fb->FlashPartition(test_partition_name, buf), SUCCESS)
<< "flash logical -partition failed";
+
+ GTEST_LOG_(INFO) << "Testing 'fastboot delete-logical-partition' command";
+ EXPECT_EQ(fb->DeletePartition(test_partition_name), SUCCESS)
+ << "delete logical-partition failed";
}
// Conformance tests
diff --git a/fastboot/socket.cpp b/fastboot/socket.cpp
index e56ffcf..5a14b63 100644
--- a/fastboot/socket.cpp
+++ b/fastboot/socket.cpp
@@ -54,7 +54,9 @@
while (total < length) {
ssize_t bytes = Receive(reinterpret_cast<char*>(data) + total, length - total, timeout_ms);
- if (bytes == -1) {
+ // Returns 0 only when the peer has disconnected because our requested length is not 0. So
+ // we return immediately to avoid dead loop here.
+ if (bytes <= 0) {
if (total == 0) {
return -1;
}
diff --git a/fastboot/usb.h b/fastboot/usb.h
index 7ca44c4..e5f56e2 100644
--- a/fastboot/usb.h
+++ b/fastboot/usb.h
@@ -50,6 +50,8 @@
char serial_number[256];
char device_path[256];
+
+ char interface[256];
};
class UsbTransport : public Transport {
diff --git a/fastboot/usb_linux.cpp b/fastboot/usb_linux.cpp
index 6363aa5..964488c 100644
--- a/fastboot/usb_linux.cpp
+++ b/fastboot/usb_linux.cpp
@@ -43,6 +43,8 @@
#include <linux/version.h>
#include <linux/usb/ch9.h>
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
#include <chrono>
#include <memory>
#include <thread>
@@ -263,6 +265,13 @@
info.has_bulk_in = (in != -1);
info.has_bulk_out = (out != -1);
+ std::string interface;
+ auto path = android::base::StringPrintf("/sys/bus/usb/devices/%s/%s:1.%d/interface",
+ sysfs_name, sysfs_name, ifc->bInterfaceNumber);
+ if (android::base::ReadFileToString(path, &interface)) {
+ snprintf(info.interface, sizeof(info.interface), "%s", interface.c_str());
+ }
+
if(callback(&info) == 0) {
*ept_in_id = in;
*ept_out_id = out;
diff --git a/fastboot/usb_osx.cpp b/fastboot/usb_osx.cpp
index 8a3c213..610eebf 100644
--- a/fastboot/usb_osx.cpp
+++ b/fastboot/usb_osx.cpp
@@ -368,6 +368,7 @@
// device has no serial number
handle->info.serial_number[0] = 0;
}
+ handle->info.interface[0] = 0;
handle->info.writable = 1;
if (try_interfaces(dev, handle)) {
diff --git a/fastboot/usb_windows.cpp b/fastboot/usb_windows.cpp
index bf840f8..67bf8a3 100644
--- a/fastboot/usb_windows.cpp
+++ b/fastboot/usb_windows.cpp
@@ -319,6 +319,7 @@
&serial_number_len, true)) {
info.serial_number[0] = 0;
}
+ info.interface[0] = 0;
info.device_path[0] = 0;
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/TEST_MAPPING b/fs_mgr/TEST_MAPPING
index 676f446..6cd0430 100644
--- a/fs_mgr/TEST_MAPPING
+++ b/fs_mgr/TEST_MAPPING
@@ -14,6 +14,9 @@
},
{
"name": "vts_libsnapshot_test"
+ },
+ {
+ "name": "libsnapshot_fuzzer_test"
}
]
}
diff --git a/fs_mgr/clean_scratch_files.rc b/fs_mgr/clean_scratch_files.rc
index 738d1aa..25a7e69 100644
--- a/fs_mgr/clean_scratch_files.rc
+++ b/fs_mgr/clean_scratch_files.rc
@@ -1,2 +1,2 @@
on post-fs-data && property:ro.debuggable=1
- exec_background - root root -- clean_scratch_files
+ exec_background - root root -- /system/bin/clean_scratch_files
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 438aeb3..6e78d17 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -62,6 +62,7 @@
#include <fs_mgr_overlayfs.h>
#include <fscrypt/fscrypt.h>
#include <libdm/dm.h>
+#include <libdm/loop_control.h>
#include <liblp/metadata_format.h>
#include <linux/fs.h>
#include <linux/loop.h>
@@ -96,6 +97,7 @@
using android::base::Basename;
using android::base::GetBoolProperty;
+using android::base::GetUintProperty;
using android::base::Realpath;
using android::base::SetProperty;
using android::base::StartsWith;
@@ -104,6 +106,7 @@
using android::dm::DeviceMapper;
using android::dm::DmDeviceState;
using android::dm::DmTargetLinear;
+using android::dm::LoopControl;
// Realistically, this file should be part of the android::fs_mgr namespace;
using namespace android::fs_mgr;
@@ -357,7 +360,7 @@
const struct ext4_super_block* sb, int* fs_stat) {
bool has_quota = (sb->s_feature_ro_compat & cpu_to_le32(EXT4_FEATURE_RO_COMPAT_QUOTA)) != 0;
bool want_quota = entry.fs_mgr_flags.quota;
- bool want_projid = android::base::GetBoolProperty("ro.emulated_storage.projid", false);
+ bool want_projid = android::base::GetBoolProperty("external_storage.projid.enabled", false);
if (has_quota == want_quota) {
return;
@@ -451,7 +454,8 @@
<< entry.encryption_options;
return;
}
- if ((options.flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64) != 0) {
+ if ((options.flags &
+ (FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64 | FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32)) != 0) {
// We can only use this policy on ext4 if the "stable_inodes" feature
// is set on the filesystem, otherwise shrinking will break encrypted files.
if ((sb->s_feature_compat & cpu_to_le32(EXT4_FEATURE_COMPAT_STABLE_INODES)) == 0) {
@@ -520,7 +524,8 @@
static void tune_casefold(const std::string& blk_device, const struct ext4_super_block* sb,
int* fs_stat) {
bool has_casefold = (sb->s_feature_incompat & cpu_to_le32(EXT4_FEATURE_INCOMPAT_CASEFOLD)) != 0;
- bool wants_casefold = android::base::GetBoolProperty("ro.emulated_storage.casefold", false);
+ bool wants_casefold =
+ android::base::GetBoolProperty("external_storage.casefold.enabled", false);
if (!wants_casefold || has_casefold) return;
@@ -1564,11 +1569,16 @@
return ret;
}
-static bool fs_mgr_unmount_all_data_mounts(const std::string& block_device) {
- LINFO << __FUNCTION__ << "(): about to umount everything on top of " << block_device;
+static std::chrono::milliseconds GetMillisProperty(const std::string& name,
+ std::chrono::milliseconds default_value) {
+ auto value = GetUintProperty(name, static_cast<uint64_t>(default_value.count()));
+ return std::chrono::milliseconds(std::move(value));
+}
+
+static bool fs_mgr_unmount_all_data_mounts(const std::string& data_block_device) {
+ LINFO << __FUNCTION__ << "(): about to umount everything on top of " << data_block_device;
Timer t;
- // TODO(b/135984674): should be configured via a read-only property.
- std::chrono::milliseconds timeout = 5s;
+ auto timeout = GetMillisProperty("init.userspace_reboot.userdata_remount.timeoutmillis", 5s);
while (true) {
bool umount_done = true;
Fstab proc_mounts;
@@ -1578,7 +1588,13 @@
}
// Now proceed with other bind mounts on top of /data.
for (const auto& entry : proc_mounts) {
- if (entry.blk_device == block_device) {
+ std::string block_device;
+ if (StartsWith(entry.blk_device, "/dev/block") &&
+ !Realpath(entry.blk_device, &block_device)) {
+ PWARNING << __FUNCTION__ << "(): failed to realpath " << entry.blk_device;
+ block_device = entry.blk_device;
+ }
+ if (data_block_device == block_device) {
if (umount2(entry.mount_point.c_str(), 0) != 0) {
PERROR << __FUNCTION__ << "(): Failed to umount " << entry.mount_point;
umount_done = false;
@@ -1590,7 +1606,8 @@
return true;
}
if (t.duration() > timeout) {
- LERROR << __FUNCTION__ << "(): Timed out unmounting all mounts on " << block_device;
+ LERROR << __FUNCTION__ << "(): Timed out unmounting all mounts on "
+ << data_block_device;
Fstab remaining_mounts;
if (!ReadFstabFromFile("/proc/mounts", &remaining_mounts)) {
LERROR << __FUNCTION__ << "(): Can't read /proc/mounts";
@@ -1607,6 +1624,58 @@
}
}
+static bool UnwindDmDeviceStack(const std::string& block_device,
+ std::vector<std::string>* dm_stack) {
+ if (!StartsWith(block_device, "/dev/block/")) {
+ LWARNING << block_device << " is not a block device";
+ return false;
+ }
+ std::string current = block_device;
+ DeviceMapper& dm = DeviceMapper::Instance();
+ while (true) {
+ dm_stack->push_back(current);
+ if (!dm.IsDmBlockDevice(current)) {
+ break;
+ }
+ auto parent = dm.GetParentBlockDeviceByPath(current);
+ if (!parent) {
+ return false;
+ }
+ current = *parent;
+ }
+ return true;
+}
+
+FstabEntry* fs_mgr_get_mounted_entry_for_userdata(Fstab* fstab,
+ const std::string& data_block_device) {
+ std::vector<std::string> dm_stack;
+ if (!UnwindDmDeviceStack(data_block_device, &dm_stack)) {
+ LERROR << "Failed to unwind dm-device stack for " << data_block_device;
+ return nullptr;
+ }
+ for (auto& entry : *fstab) {
+ if (entry.mount_point != "/data") {
+ continue;
+ }
+ std::string block_device;
+ if (entry.fs_mgr_flags.logical) {
+ if (!fs_mgr_update_logical_partition(&entry)) {
+ LERROR << "Failed to update logic partition " << entry.blk_device;
+ continue;
+ }
+ block_device = entry.blk_device;
+ } else if (!Realpath(entry.blk_device, &block_device)) {
+ PWARNING << "Failed to realpath " << entry.blk_device;
+ block_device = entry.blk_device;
+ }
+ if (std::find(dm_stack.begin(), dm_stack.end(), block_device) != dm_stack.end()) {
+ return &entry;
+ }
+ }
+ LERROR << "Didn't find entry that was used to mount /data onto " << data_block_device;
+ return nullptr;
+}
+
// TODO(b/143970043): return different error codes based on which step failed.
int fs_mgr_remount_userdata_into_checkpointing(Fstab* fstab) {
Fstab proc_mounts;
@@ -1614,17 +1683,17 @@
LERROR << "Can't read /proc/mounts";
return -1;
}
- std::string block_device;
- if (auto entry = GetEntryForMountPoint(&proc_mounts, "/data"); entry != nullptr) {
- // Note: we don't care about a userdata wrapper here, since it's safe
- // to remount on top of the bow device instead, there will be no
- // conflicts.
- block_device = entry->blk_device;
- } else {
+ auto mounted_entry = GetEntryForMountPoint(&proc_mounts, "/data");
+ if (mounted_entry == nullptr) {
LERROR << "/data is not mounted";
return -1;
}
- auto fstab_entry = GetMountedEntryForUserdata(fstab);
+ std::string block_device;
+ if (!Realpath(mounted_entry->blk_device, &block_device)) {
+ PERROR << "Failed to realpath " << mounted_entry->blk_device;
+ return -1;
+ }
+ auto fstab_entry = fs_mgr_get_mounted_entry_for_userdata(fstab, block_device);
if (fstab_entry == nullptr) {
LERROR << "Can't find /data in fstab";
return -1;
@@ -1862,19 +1931,6 @@
return InstallZramDevice(bdev);
}
- // Get free loopback
- unique_fd loop_fd(TEMP_FAILURE_RETRY(open("/dev/loop-control", O_RDWR | O_CLOEXEC)));
- if (loop_fd.get() == -1) {
- PERROR << "Cannot open loop-control";
- return false;
- }
-
- int num = ioctl(loop_fd.get(), LOOP_CTL_GET_FREE);
- if (num == -1) {
- PERROR << "Cannot get free loop slot";
- return false;
- }
-
// Prepare target path
unique_fd target_fd(TEMP_FAILURE_RETRY(open(loop.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0600)));
if (target_fd.get() == -1) {
@@ -1886,25 +1942,21 @@
return false;
}
- // Connect loopback (device_fd) to target path (target_fd)
- std::string device = android::base::StringPrintf("/dev/block/loop%d", num);
- unique_fd device_fd(TEMP_FAILURE_RETRY(open(device.c_str(), O_RDWR | O_CLOEXEC)));
- if (device_fd.get() == -1) {
- PERROR << "Cannot open /dev/block/loop" << num;
- return false;
- }
-
- if (ioctl(device_fd.get(), LOOP_SET_FD, target_fd.get())) {
- PERROR << "Cannot set loopback to target path";
+ // Allocate loop device and attach it to file_path.
+ LoopControl loop_control;
+ std::string device;
+ if (!loop_control.Attach(target_fd.get(), 5s, &device)) {
return false;
}
// set block size & direct IO
- if (ioctl(device_fd.get(), LOOP_SET_BLOCK_SIZE, 4096)) {
- PWARNING << "Cannot set 4KB blocksize to /dev/block/loop" << num;
+ unique_fd device_fd(TEMP_FAILURE_RETRY(open(device.c_str(), O_RDWR | O_CLOEXEC)));
+ if (device_fd.get() == -1) {
+ PERROR << "Cannot open " << device;
+ return false;
}
- if (ioctl(device_fd.get(), LOOP_SET_DIRECT_IO, 1)) {
- PWARNING << "Cannot set direct_io to /dev/block/loop" << num;
+ if (!LoopControl::EnableDirectIo(device_fd.get())) {
+ return false;
}
return InstallZramDevice(device);
diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp
index ea9c957..9046132 100644
--- a/fs_mgr/fs_mgr_dm_linear.cpp
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -52,17 +52,27 @@
using DmTargetZero = android::dm::DmTargetZero;
using DmTargetLinear = android::dm::DmTargetLinear;
-static bool GetPhysicalPartitionDevicePath(const IPartitionOpener& opener,
- const LpMetadata& metadata,
+static bool GetPhysicalPartitionDevicePath(const CreateLogicalPartitionParams& params,
const LpMetadataBlockDevice& block_device,
const std::string& super_device, std::string* result) {
// If the super device is the source of this block device's metadata,
// make sure we use the correct super device (and not just "super",
// which might not exist.)
std::string name = GetBlockDevicePartitionName(block_device);
- std::string dev_string = opener.GetDeviceString(name);
- if (GetMetadataSuperBlockDevice(metadata) == &block_device) {
- dev_string = opener.GetDeviceString(super_device);
+ if (android::base::StartsWith(name, "dm-")) {
+ // Device-mapper nodes are not normally allowed in LpMetadata, since
+ // they are not consistent across reboots. However for the purposes of
+ // testing it's useful to handle them. For example when running DSUs,
+ // userdata is a device-mapper device, and some stacking will result
+ // when using libfiemap.
+ *result = "/dev/block/" + name;
+ return true;
+ }
+
+ auto opener = params.partition_opener;
+ std::string dev_string = opener->GetDeviceString(name);
+ if (GetMetadataSuperBlockDevice(*params.metadata) == &block_device) {
+ dev_string = opener->GetDeviceString(super_device);
}
// Note: device-mapper will not accept symlinks, so we must use realpath
@@ -93,8 +103,8 @@
case LP_TARGET_TYPE_LINEAR: {
const auto& block_device = params.metadata->block_devices[extent.target_source];
std::string dev_string;
- if (!GetPhysicalPartitionDevicePath(*params.partition_opener, *params.metadata,
- block_device, super_device, &dev_string)) {
+ if (!GetPhysicalPartitionDevicePath(params, block_device, super_device,
+ &dev_string)) {
LOG(ERROR) << "Unable to complete device-mapper table, unknown block device";
return false;
}
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
index 7be024f..301c907 100644
--- a/fs_mgr/fs_mgr_format.cpp
+++ b/fs_mgr/fs_mgr_format.cpp
@@ -121,7 +121,7 @@
}
static int format_f2fs(const std::string& fs_blkdev, uint64_t dev_sz, bool crypt_footer,
- bool needs_projid, bool needs_casefold) {
+ bool needs_projid, bool needs_casefold, bool fs_compress) {
if (!dev_sz) {
int rc = get_dev_sz(fs_blkdev, &dev_sz);
if (rc) {
@@ -147,6 +147,12 @@
args.push_back("-C");
args.push_back("utf8");
}
+ if (fs_compress) {
+ args.push_back("-O");
+ args.push_back("compression");
+ args.push_back("-O");
+ args.push_back("extra_attr");
+ }
args.push_back(fs_blkdev.c_str());
args.push_back(size_str.c_str());
@@ -160,13 +166,13 @@
bool needs_projid = false;
if (entry.mount_point == "/data") {
- needs_casefold = android::base::GetBoolProperty("ro.emulated_storage.casefold", false);
- needs_projid = android::base::GetBoolProperty("ro.emulated_storage.projid", false);
+ needs_casefold = android::base::GetBoolProperty("external_storage.casefold.enabled", false);
+ needs_projid = android::base::GetBoolProperty("external_storage.projid.enabled", false);
}
if (entry.fs_type == "f2fs") {
return format_f2fs(entry.blk_device, entry.length, crypt_footer, needs_projid,
- needs_casefold);
+ needs_casefold, entry.fs_mgr_flags.fs_compress);
} else if (entry.fs_type == "ext4") {
return format_ext4(entry.blk_device, entry.mount_point, crypt_footer, needs_projid,
entry.fs_mgr_flags.ext_meta_csum);
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 4ebe085..f333a85 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -178,6 +178,7 @@
CheckFlag("slotselect_other", slot_select_other);
CheckFlag("fsverity", fs_verity);
CheckFlag("metadata_csum", ext_meta_csum);
+ CheckFlag("fscompress", fs_compress);
#undef CheckFlag
@@ -407,16 +408,17 @@
return fstab_result;
}
-// Identify path to fstab file. Lookup is based on pattern fstab.<hardware>,
-// fstab.<hardware.platform> in folders /odm/etc, vendor/etc, or /.
+// Identify path to fstab file. Lookup is based on pattern
+// fstab.<fstab_suffix>, fstab.<hardware>, fstab.<hardware.platform> in
+// folders /odm/etc, vendor/etc, or /.
std::string GetFstabPath() {
- for (const char* prop : {"hardware", "hardware.platform"}) {
- std::string hw;
+ for (const char* prop : {"fstab_suffix", "hardware", "hardware.platform"}) {
+ std::string suffix;
- if (!fs_mgr_get_boot_config(prop, &hw)) continue;
+ if (!fs_mgr_get_boot_config(prop, &suffix)) continue;
for (const char* prefix : {"/odm/etc/fstab.", "/vendor/etc/fstab.", "/fstab."}) {
- std::string fstab_path = prefix + hw;
+ std::string fstab_path = prefix + suffix;
if (access(fstab_path.c_str(), F_OK) == 0) {
return fstab_path;
}
@@ -819,89 +821,6 @@
return entries;
}
-static std::string ResolveBlockDevice(const std::string& block_device) {
- if (!StartsWith(block_device, "/dev/block/")) {
- LWARNING << block_device << " is not a block device";
- return block_device;
- }
- std::string name = block_device.substr(5);
- if (!StartsWith(name, "block/dm-")) {
- // Not a dm-device, but might be a symlink. Optimistically try to readlink.
- std::string result;
- if (Readlink(block_device, &result)) {
- return result;
- } else if (errno == EINVAL) {
- // After all, it wasn't a symlink.
- return block_device;
- } else {
- LERROR << "Failed to readlink " << block_device;
- return "";
- }
- }
- // It's a dm-device, let's find what's inside!
- std::string sys_dir = "/sys/" + name;
- while (true) {
- std::string slaves_dir = sys_dir + "/slaves";
- std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(slaves_dir.c_str()), closedir);
- if (!dir) {
- LERROR << "Failed to open " << slaves_dir;
- return "";
- }
- std::string sub_device_name = "";
- for (auto entry = readdir(dir.get()); entry; entry = readdir(dir.get())) {
- if (entry->d_type != DT_LNK) continue;
- if (!sub_device_name.empty()) {
- LERROR << "Too many slaves in " << slaves_dir;
- return "";
- }
- sub_device_name = entry->d_name;
- }
- if (sub_device_name.empty()) {
- LERROR << "No slaves in " << slaves_dir;
- return "";
- }
- if (!StartsWith(sub_device_name, "dm-")) {
- // Not a dm-device! We can stop now.
- return "/dev/block/" + sub_device_name;
- }
- // Still a dm-device, keep digging.
- sys_dir = "/sys/block/" + sub_device_name;
- }
-}
-
-FstabEntry* GetMountedEntryForUserdata(Fstab* fstab) {
- Fstab mounts;
- if (!ReadFstabFromFile("/proc/mounts", &mounts)) {
- LERROR << "Failed to read /proc/mounts";
- return nullptr;
- }
- auto mounted_entry = GetEntryForMountPoint(&mounts, "/data");
- if (mounted_entry == nullptr) {
- LWARNING << "/data is not mounted";
- return nullptr;
- }
- std::string resolved_block_device = ResolveBlockDevice(mounted_entry->blk_device);
- if (resolved_block_device.empty()) {
- return nullptr;
- }
- LINFO << "/data is mounted on " << resolved_block_device;
- for (auto& entry : *fstab) {
- if (entry.mount_point != "/data") {
- continue;
- }
- std::string block_device;
- if (!Readlink(entry.blk_device, &block_device)) {
- LWARNING << "Failed to readlink " << entry.blk_device;
- block_device = entry.blk_device;
- }
- if (block_device == resolved_block_device) {
- return &entry;
- }
- }
- LERROR << "Didn't find entry that was used to mount /data";
- return nullptr;
-}
-
std::set<std::string> GetBootDevices() {
// First check the kernel commandline, then try the device tree otherwise
std::string dt_file_name = get_android_dt_dir() + "/boot_devices";
@@ -912,6 +831,20 @@
return std::set<std::string>(boot_devices.begin(), boot_devices.end());
}
+ std::string cmdline;
+ if (android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
+ std::set<std::string> boot_devices;
+ const std::string cmdline_key = "androidboot.boot_device";
+ for (const auto& [key, value] : fs_mgr_parse_boot_config(cmdline)) {
+ if (key == cmdline_key) {
+ boot_devices.emplace(value);
+ }
+ }
+ if (!boot_devices.empty()) {
+ return boot_devices;
+ }
+ }
+
// Fallback to extract boot devices from fstab.
Fstab fstab;
if (!ReadDefaultFstab(&fstab)) {
diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp
index 24cbad7..052efa7 100644
--- a/fs_mgr/fs_mgr_remount.cpp
+++ b/fs_mgr/fs_mgr_remount.cpp
@@ -105,20 +105,23 @@
} // namespace
+enum RemountStatus {
+ REMOUNT_SUCCESS = 0,
+ NOT_USERDEBUG,
+ BADARG,
+ NOT_ROOT,
+ NO_FSTAB,
+ UNKNOWN_PARTITION,
+ INVALID_PARTITION,
+ VERITY_PARTITION,
+ BAD_OVERLAY,
+ NO_MOUNTS,
+ REMOUNT_FAILED,
+ MUST_REBOOT
+};
+
static int do_remount(int argc, char* argv[]) {
- enum {
- SUCCESS = 0,
- NOT_USERDEBUG,
- BADARG,
- NOT_ROOT,
- NO_FSTAB,
- UNKNOWN_PARTITION,
- INVALID_PARTITION,
- VERITY_PARTITION,
- BAD_OVERLAY,
- NO_MOUNTS,
- REMOUNT_FAILED,
- } retval = SUCCESS;
+ RemountStatus retval = REMOUNT_SUCCESS;
// If somehow this executable is delivered on a "user" build, it can
// not function, so providing a clear message to the caller rather than
@@ -304,8 +307,7 @@
if (partitions.empty() || just_disabled_verity) {
if (reboot_later) reboot(setup_overlayfs);
if (user_please_reboot_later) {
- LOG(INFO) << "Now reboot your device for settings to take effect";
- return 0;
+ return MUST_REBOOT;
}
LOG(WARNING) << "No partitions to remount";
return retval;
@@ -394,6 +396,12 @@
int main(int argc, char* argv[]) {
android::base::InitLogging(argv, MyLogger);
int result = do_remount(argc, argv);
- printf("remount %s\n", result ? "failed" : "succeeded");
+ if (result == MUST_REBOOT) {
+ LOG(INFO) << "Now reboot your device for settings to take effect";
+ } else if (result == REMOUNT_SUCCESS) {
+ printf("remount succeeded\n");
+ } else {
+ printf("remount failed\n");
+ }
return result;
}
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 9bc38f9..86090c1 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -107,6 +107,9 @@
// it destroys verity devices from device mapper after the device is unmounted.
int fs_mgr_umount_all(android::fs_mgr::Fstab* fstab);
+// Finds a entry in |fstab| that was used to mount a /data on |data_block_device|.
+android::fs_mgr::FstabEntry* fs_mgr_get_mounted_entry_for_userdata(
+ android::fs_mgr::Fstab* fstab, const std::string& data_block_device);
int fs_mgr_remount_userdata_into_checkpointing(android::fs_mgr::Fstab* fstab);
// Finds the dm_bow device on which this block device is stacked, or returns
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 05e18fa..7216402 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -84,6 +84,7 @@
bool slot_select_other : 1;
bool fs_verity : 1;
bool ext_meta_csum : 1;
+ bool fs_compress : 1;
} fs_mgr_flags = {};
bool is_encryptable() const {
@@ -104,7 +105,6 @@
FstabEntry* GetEntryForMountPoint(Fstab* fstab, const std::string& path);
// The Fstab can contain multiple entries for the same mount point with different configurations.
std::vector<FstabEntry*> GetEntriesForMountPoint(Fstab* fstab, const std::string& path);
-FstabEntry* GetMountedEntryForUserdata(Fstab* fstab);
// This method builds DSU fstab entries and transfer the fstab.
//
diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp
index 1c3427f..58241b3 100644
--- a/fs_mgr/libdm/Android.bp
+++ b/fs_mgr/libdm/Android.bp
@@ -58,11 +58,11 @@
defaults: ["fs_mgr_defaults"],
static_libs: [
"libdm",
+ "libext2_uuid",
+ "libfs_mgr",
],
shared_libs: [
"libbase",
- "libext2_uuid",
- "libfs_mgr",
"liblog",
],
srcs: [":libdm_test_srcs"],
@@ -79,22 +79,26 @@
cc_test {
name: "vts_libdm_test",
defaults: ["libdm_test_defaults"],
- test_suites: ["vts-core"],
+ test_suites: ["vts"],
test_min_api_level: 29,
}
cc_fuzz {
- name: "dm_linear_table_fuzzer",
- defaults: ["fs_mgr_defaults"],
- srcs: [
- "dm_linear_fuzzer.cpp",
- "test_util.cpp",
- ],
- static_libs: [
+ name: "dm_linear_table_fuzzer",
+ defaults: ["fs_mgr_defaults"],
+ srcs: [
+ "dm_linear_fuzzer.cpp",
+ "test_util.cpp",
+ ],
+ static_libs: [
"libdm",
"libbase",
"libext2_uuid",
"libfs_mgr",
"liblog",
- ],
+ ],
+}
+
+vts_config {
+ name: "VtsKernelLibdmTest",
}
diff --git a/fs_mgr/libdm/Android.mk b/fs_mgr/libdm/Android.mk
deleted file mode 100644
index 6aedc25..0000000
--- a/fs_mgr/libdm/Android.mk
+++ /dev/null
@@ -1,22 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := VtsKernelLibdmTest
--include test/vts/tools/build/Android.host_config.mk
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index 254fbed..7912688 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -29,6 +29,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/macros.h>
+#include <android-base/properties.h>
#include <android-base/strings.h>
#include <uuid/uuid.h>
@@ -120,7 +121,7 @@
return false;
}
if (!WaitForFileDeleted(unique_path, timeout_ms)) {
- LOG(ERROR) << "Timeout out waiting for " << unique_path << " to be deleted";
+ LOG(ERROR) << "Failed waiting for " << unique_path << " to be deleted";
return false;
}
return true;
@@ -140,6 +141,10 @@
return std::string{uuid_chars};
}
+static bool IsRecovery() {
+ return access("/system/bin/recovery", F_OK) == 0;
+}
+
bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table, std::string* path,
const std::chrono::milliseconds& timeout_ms) {
std::string uuid = GenerateUuid();
@@ -160,8 +165,18 @@
if (timeout_ms <= std::chrono::milliseconds::zero()) {
return true;
}
+
+ if (IsRecovery()) {
+ bool non_ab_device = android::base::GetProperty("ro.build.ab_update", "").empty();
+ int sdk = android::base::GetIntProperty("ro.build.version.sdk", 0);
+ if (non_ab_device && sdk && sdk <= 29) {
+ LOG(INFO) << "Detected ueventd incompatibility, reverting to legacy libdm behavior.";
+ unique_path = *path;
+ }
+ }
+
if (!WaitForFile(unique_path, timeout_ms)) {
- LOG(ERROR) << "Timed out waiting for device path: " << unique_path;
+ LOG(ERROR) << "Failed waiting for device path: " << unique_path;
DeleteDevice(name);
return false;
}
diff --git a/fs_mgr/libdm/dm_target.cpp b/fs_mgr/libdm/dm_target.cpp
index 38e4b82..250cb82 100644
--- a/fs_mgr/libdm/dm_target.cpp
+++ b/fs_mgr/libdm/dm_target.cpp
@@ -248,20 +248,8 @@
return android::base::Join(argv, " ");
}
-bool DmTargetDefaultKey::IsLegacy(bool* result) {
- DeviceMapper& dm = DeviceMapper::Instance();
- DmTargetTypeInfo info;
- if (!dm.GetTargetByName(kName, &info)) return false;
- // dm-default-key was modified to be like dm-crypt with version 2
- *result = !info.IsAtLeast(2, 0, 0);
- return true;
-}
-
bool DmTargetDefaultKey::Valid() const {
- bool real_is_legacy;
- if (!DmTargetDefaultKey::IsLegacy(&real_is_legacy)) return false;
- if (real_is_legacy != is_legacy_) return false;
- if (!is_legacy_ && !set_dun_) return false;
+ if (!use_legacy_options_format_ && !set_dun_) return false;
return true;
}
@@ -269,13 +257,13 @@
std::vector<std::string> argv;
argv.emplace_back(cipher_);
argv.emplace_back(key_);
- if (!is_legacy_) {
+ if (!use_legacy_options_format_) {
argv.emplace_back("0"); // iv_offset
}
argv.emplace_back(blockdev_);
argv.push_back(std::to_string(start_sector_));
std::vector<std::string> extra_argv;
- if (is_legacy_) {
+ if (use_legacy_options_format_) {
if (set_dun_) { // v2 always sets the DUN.
extra_argv.emplace_back("set_dun");
}
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
index 67af59a..41d3145 100644
--- a/fs_mgr/libdm/dm_test.cpp
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -516,32 +516,22 @@
}
TEST(libdm, DefaultKeyArgs) {
- DmTargetTypeInfo info;
-
- DeviceMapper& dm = DeviceMapper::Instance();
- if (!dm.GetTargetByName("default-key", &info)) {
- cout << "default-key module not enabled; skipping test" << std::endl;
- return;
- }
- bool is_legacy;
- ASSERT_TRUE(DmTargetDefaultKey::IsLegacy(&is_legacy));
- // set_dun only in the non-is_legacy case
- DmTargetDefaultKey target(0, 4096, "AES-256-XTS", "abcdef0123456789", "/dev/loop0", 0);
- if (is_legacy) {
- target.SetIsLegacy();
- } else {
- target.SetSetDun();
- }
+ DmTargetDefaultKey target(0, 4096, "aes-xts-plain64", "abcdef0123456789", "/dev/loop0", 0);
+ target.SetSetDun();
ASSERT_EQ(target.name(), "default-key");
ASSERT_TRUE(target.Valid());
- if (is_legacy) {
- ASSERT_EQ(target.GetParameterString(), "AES-256-XTS abcdef0123456789 /dev/loop0 0");
- } else {
- // TODO: Add case for wrapped key enabled
- ASSERT_EQ(target.GetParameterString(),
- "AES-256-XTS abcdef0123456789 0 /dev/loop0 0 3 allow_discards sector_size:4096 "
- "iv_large_sectors");
- }
+ // TODO: Add case for wrapped key enabled
+ ASSERT_EQ(target.GetParameterString(),
+ "aes-xts-plain64 abcdef0123456789 0 /dev/loop0 0 3 allow_discards sector_size:4096 "
+ "iv_large_sectors");
+}
+
+TEST(libdm, DefaultKeyLegacyArgs) {
+ DmTargetDefaultKey target(0, 4096, "AES-256-XTS", "abcdef0123456789", "/dev/loop0", 0);
+ target.SetUseLegacyOptionsFormat();
+ ASSERT_EQ(target.name(), "default-key");
+ ASSERT_TRUE(target.Valid());
+ ASSERT_EQ(target.GetParameterString(), "AES-256-XTS abcdef0123456789 /dev/loop0 0");
}
TEST(libdm, DeleteDeviceWithTimeout) {
diff --git a/fs_mgr/libdm/include/libdm/dm_target.h b/fs_mgr/libdm/include/libdm/dm_target.h
index 875efe9..f986cfe 100644
--- a/fs_mgr/libdm/include/libdm/dm_target.h
+++ b/fs_mgr/libdm/include/libdm/dm_target.h
@@ -293,8 +293,7 @@
std::string name() const override { return kName; }
bool Valid() const override;
std::string GetParameterString() const override;
- static bool IsLegacy(bool* result);
- void SetIsLegacy() { is_legacy_ = true; }
+ void SetUseLegacyOptionsFormat() { use_legacy_options_format_ = true; }
void SetSetDun() { set_dun_ = true; }
void SetWrappedKeyV0() { is_hw_wrapped_ = true; }
@@ -305,7 +304,7 @@
std::string key_;
std::string blockdev_;
uint64_t start_sector_;
- bool is_legacy_ = false;
+ bool use_legacy_options_format_ = false;
bool set_dun_ = false;
bool is_hw_wrapped_ = false;
};
diff --git a/fs_mgr/libdm/utility.cpp b/fs_mgr/libdm/utility.cpp
index f252565..0eb59ab 100644
--- a/fs_mgr/libdm/utility.cpp
+++ b/fs_mgr/libdm/utility.cpp
@@ -19,6 +19,8 @@
#include <thread>
+#include <android-base/logging.h>
+
using namespace std::literals;
namespace android {
@@ -45,7 +47,11 @@
// If the file exists but returns EPERM or something, we consider the
// condition met.
if (access(path.c_str(), F_OK) != 0) {
- if (errno == ENOENT) return WaitResult::Wait;
+ if (errno == ENOENT) {
+ return WaitResult::Wait;
+ }
+ PLOG(ERROR) << "access failed: " << path;
+ return WaitResult::Fail;
}
return WaitResult::Done;
};
@@ -54,9 +60,13 @@
bool WaitForFileDeleted(const std::string& path, const std::chrono::milliseconds& timeout_ms) {
auto condition = [&]() -> WaitResult {
- if (access(path.c_str(), F_OK) == 0 || errno != ENOENT) {
+ if (access(path.c_str(), F_OK) == 0) {
return WaitResult::Wait;
}
+ if (errno != ENOENT) {
+ PLOG(ERROR) << "access failed: " << path;
+ return WaitResult::Fail;
+ }
return WaitResult::Done;
};
return WaitForCondition(condition, timeout_ms);
diff --git a/fs_mgr/libfiemap/Android.bp b/fs_mgr/libfiemap/Android.bp
index 2fd463c..cae43e6 100644
--- a/fs_mgr/libfiemap/Android.bp
+++ b/fs_mgr/libfiemap/Android.bp
@@ -45,6 +45,7 @@
whole_static_libs: [
"gsi_aidl_interface-cpp",
"libgsi",
+ "libgsid",
],
shared_libs: [
"libbinder",
@@ -68,6 +69,7 @@
"libdm",
"libfs_mgr",
"liblog",
+ "libgsi",
],
data: [
@@ -80,7 +82,7 @@
"fiemap_writer_test.cpp",
],
- test_suites: ["vts-core", "device-tests"],
+ test_suites: ["vts", "device-tests"],
auto_gen_config: true,
test_min_api_level: 29,
require_root: true,
@@ -89,6 +91,7 @@
cc_test {
name: "fiemap_image_test",
static_libs: [
+ "libcrypto_utils",
"libdm",
"libext4_utils",
"libfs_mgr",
@@ -97,7 +100,6 @@
shared_libs: [
"libbase",
"libcrypto",
- "libcrypto_utils",
"libcutils",
"liblog",
],
@@ -116,6 +118,7 @@
"-DSKIP_TEST_IN_PRESUBMIT",
],
static_libs: [
+ "libcrypto_utils",
"libdm",
"libext4_utils",
"libfs_mgr",
@@ -124,7 +127,6 @@
shared_libs: [
"libbase",
"libcrypto",
- "libcrypto_utils",
"libcutils",
"liblog",
],
diff --git a/fs_mgr/libfiemap/binder.cpp b/fs_mgr/libfiemap/binder.cpp
index 96c36ed..c8516ab 100644
--- a/fs_mgr/libfiemap/binder.cpp
+++ b/fs_mgr/libfiemap/binder.cpp
@@ -19,10 +19,9 @@
#include <android-base/properties.h>
#include <android/gsi/BnProgressCallback.h>
#include <android/gsi/IGsiService.h>
-#include <android/gsi/IGsid.h>
-#include <binder/IServiceManager.h>
#include <libfiemap/image_manager.h>
#include <libgsi/libgsi.h>
+#include <libgsi/libgsid.h>
namespace android {
namespace fiemap {
@@ -225,54 +224,12 @@
return false;
}
-static android::sp<IGsid> AcquireIGsid(const std::chrono::milliseconds& timeout_ms) {
- if (android::base::GetProperty("init.svc.gsid", "") != "running") {
- if (!android::base::SetProperty("ctl.start", "gsid") ||
- !android::base::WaitForProperty("init.svc.gsid", "running", timeout_ms)) {
- LOG(ERROR) << "Could not start the gsid service";
- return nullptr;
- }
- // Sleep for 250ms to give the service time to register.
- usleep(250 * 1000);
- }
- auto sm = android::defaultServiceManager();
- auto name = android::String16(kGsiServiceName);
- auto service = sm->checkService(name);
- return android::interface_cast<IGsid>(service);
-}
-
-static android::sp<IGsid> GetGsiService(const std::chrono::milliseconds& timeout_ms) {
- auto start_time = std::chrono::steady_clock::now();
-
- std::chrono::milliseconds elapsed = std::chrono::milliseconds::zero();
- do {
- if (auto gsid = AcquireIGsid(timeout_ms - elapsed); gsid != nullptr) {
- return gsid;
- }
- auto now = std::chrono::steady_clock::now();
- elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
- } while (elapsed <= timeout_ms);
-
- LOG(ERROR) << "Timed out trying to acquire IGsid interface";
- return nullptr;
-}
-
-std::unique_ptr<IImageManager> IImageManager::Open(const std::string& dir,
- const std::chrono::milliseconds& timeout_ms) {
- auto gsid = GetGsiService(timeout_ms);
- if (!gsid) {
- return nullptr;
- }
-
- android::sp<IGsiService> service;
- auto status = gsid->getClient(&service);
- if (!status.isOk() || !service) {
- LOG(ERROR) << "Could not acquire IGsiService";
- return nullptr;
- }
-
+std::unique_ptr<IImageManager> IImageManager::Open(
+ const std::string& dir, const std::chrono::milliseconds& /*timeout_ms*/) {
+ android::sp<IGsiService> service = android::gsi::GetGsiService();
android::sp<IImageService> manager;
- status = service->openImageService(dir, &manager);
+
+ auto status = service->openImageService(dir, &manager);
if (!status.isOk() || !manager) {
LOG(ERROR) << "Could not acquire IImageManager: " << status.exceptionMessage().string();
return nullptr;
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/libfiemap/image_manager.cpp b/fs_mgr/libfiemap/image_manager.cpp
index 6717922..3ee742f 100644
--- a/fs_mgr/libfiemap/image_manager.cpp
+++ b/fs_mgr/libfiemap/image_manager.cpp
@@ -51,6 +51,7 @@
using android::fs_mgr::GetPartitionName;
static constexpr char kTestImageMetadataDir[] = "/metadata/gsi/test";
+static constexpr char kOtaTestImageMetadataDir[] = "/metadata/gsi/ota/test";
std::unique_ptr<ImageManager> ImageManager::Open(const std::string& dir_prefix) {
auto metadata_dir = "/metadata/gsi/" + dir_prefix;
@@ -135,10 +136,13 @@
return !!FindPartition(*metadata.get(), name);
}
+static bool IsTestDir(const std::string& path) {
+ return android::base::StartsWith(path, kTestImageMetadataDir) ||
+ android::base::StartsWith(path, kOtaTestImageMetadataDir);
+}
+
static bool IsUnreliablePinningAllowed(const std::string& path) {
- return android::base::StartsWith(path, "/data/gsi/dsu/") ||
- android::base::StartsWith(path, "/data/gsi/test/") ||
- android::base::StartsWith(path, "/data/gsi/ota/test/");
+ return android::base::StartsWith(path, "/data/gsi/dsu/") || IsTestDir(path);
}
FiemapStatus ImageManager::CreateBackingImage(
@@ -174,8 +178,7 @@
// if device-mapper is stacked in some complex way not supported by
// FiemapWriter.
auto device_path = GetDevicePathForFile(fw.get());
- if (android::base::StartsWith(device_path, "/dev/block/dm-") &&
- !android::base::StartsWith(metadata_dir_, kTestImageMetadataDir)) {
+ if (android::base::StartsWith(device_path, "/dev/block/dm-") && !IsTestDir(metadata_dir_)) {
LOG(ERROR) << "Cannot persist images against device-mapper device: " << device_path;
fw = {};
diff --git a/fs_mgr/libfiemap/image_test.cpp b/fs_mgr/libfiemap/image_test.cpp
index 5388b44..6663391 100644
--- a/fs_mgr/libfiemap/image_test.cpp
+++ b/fs_mgr/libfiemap/image_test.cpp
@@ -131,132 +131,6 @@
ASSERT_TRUE(manager_->UnmapImageDevice(base_name_));
}
-// This fixture is for tests against a simulated device environment. Rather
-// than use /data, we create an image and then layer a new filesystem within
-// it. Each test then decides how to mount and create layered images. This
-// allows us to test FBE vs FDE configurations.
-class ImageTest : public ::testing::Test {
- public:
- ImageTest() : dm_(DeviceMapper::Instance()) {}
-
- void SetUp() override {
- manager_ = ImageManager::Open(kMetadataPath, gDataPath);
- ASSERT_NE(manager_, nullptr);
-
- manager_->set_partition_opener(std::make_unique<TestPartitionOpener>());
-
- submanager_ = ImageManager::Open(kMetadataPath + "/mnt"s, gDataPath + "/mnt"s);
- ASSERT_NE(submanager_, nullptr);
-
- submanager_->set_partition_opener(std::make_unique<TestPartitionOpener>());
-
- // Ensure that metadata is cleared in between runs.
- submanager_->RemoveAllImages();
- manager_->RemoveAllImages();
-
- const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();
- base_name_ = tinfo->name();
- test_image_name_ = base_name_ + "-base";
- wrapper_device_name_ = base_name_ + "-wrapper";
-
- ASSERT_TRUE(manager_->CreateBackingImage(base_name_, kTestImageSize * 16, false, nullptr));
- ASSERT_TRUE(manager_->MapImageDevice(base_name_, 5s, &base_device_));
- }
-
- void TearDown() override {
- submanager_->UnmapImageDevice(test_image_name_);
- umount(gDataMountPath.c_str());
- dm_.DeleteDeviceIfExists(wrapper_device_name_);
- manager_->UnmapImageDevice(base_name_);
- manager_->DeleteBackingImage(base_name_);
- }
-
- protected:
- bool DoFormat(const std::string& device) {
- // clang-format off
- std::vector<std::string> mkfs_args = {
- "/system/bin/mke2fs",
- "-F",
- "-b 4096",
- "-t ext4",
- "-m 0",
- "-O has_journal",
- device,
- ">/dev/null",
- "2>/dev/null",
- "</dev/null",
- };
- // clang-format on
- auto command = android::base::Join(mkfs_args, " ");
- return system(command.c_str()) == 0;
- }
-
- std::unique_ptr<ImageManager> manager_;
- std::unique_ptr<ImageManager> submanager_;
-
- DeviceMapper& dm_;
- std::string base_name_;
- std::string base_device_;
- std::string test_image_name_;
- std::string wrapper_device_name_;
-};
-
-TEST_F(ImageTest, DirectMount) {
- ASSERT_TRUE(DoFormat(base_device_));
- ASSERT_EQ(mount(base_device_.c_str(), gDataMountPath.c_str(), "ext4", 0, nullptr), 0);
- ASSERT_TRUE(submanager_->CreateBackingImage(test_image_name_, kTestImageSize, false, nullptr));
-
- std::string path;
- ASSERT_TRUE(submanager_->MapImageDevice(test_image_name_, 5s, &path));
- ASSERT_TRUE(android::base::StartsWith(path, "/dev/block/loop"));
-}
-
-TEST_F(ImageTest, IndirectMount) {
-#ifdef SKIP_TEST_IN_PRESUBMIT
- GTEST_SKIP() << "WIP failure b/148874852";
-#endif
- // Create a simple wrapper around the base device that we'll mount from
- // instead. This will simulate the code paths for dm-crypt/default-key/bow
- // and force us to use device-mapper rather than loop devices.
- uint64_t device_size = 0;
- {
- unique_fd fd(open(base_device_.c_str(), O_RDWR | O_CLOEXEC));
- ASSERT_GE(fd, 0);
- device_size = get_block_device_size(fd);
- ASSERT_EQ(device_size, kTestImageSize * 16);
- }
- uint64_t num_sectors = device_size / 512;
-
- auto& dm = DeviceMapper::Instance();
-
- DmTable table;
- table.Emplace<DmTargetLinear>(0, num_sectors, base_device_, 0);
- ASSERT_TRUE(dm.CreateDevice(wrapper_device_name_, table));
-
- // Format and mount.
- std::string wrapper_device;
- ASSERT_TRUE(dm.GetDmDevicePathByName(wrapper_device_name_, &wrapper_device));
- ASSERT_TRUE(WaitForFile(wrapper_device, 5s));
- ASSERT_TRUE(DoFormat(wrapper_device));
- ASSERT_EQ(mount(wrapper_device.c_str(), gDataMountPath.c_str(), "ext4", 0, nullptr), 0);
-
- ASSERT_TRUE(submanager_->CreateBackingImage(test_image_name_, kTestImageSize, false, nullptr));
-
- std::set<std::string> backing_devices;
- auto init = [&](std::set<std::string> devices) -> bool {
- backing_devices = std::move(devices);
- return true;
- };
-
- std::string path;
- ASSERT_TRUE(submanager_->MapImageDevice(test_image_name_, 5s, &path));
- ASSERT_TRUE(android::base::StartsWith(path, "/dev/block/dm-"));
- ASSERT_TRUE(submanager_->UnmapImageDevice(test_image_name_));
- ASSERT_TRUE(submanager_->MapAllImages(init));
- ASSERT_FALSE(backing_devices.empty());
- ASSERT_TRUE(submanager_->UnmapImageDevice(test_image_name_));
-}
-
bool Mkdir(const std::string& path) {
if (mkdir(path.c_str(), 0700) && errno != EEXIST) {
std::cerr << "Could not mkdir " << path << ": " << strerror(errno) << std::endl;
diff --git a/fs_mgr/libfs_avb/avb_util.cpp b/fs_mgr/libfs_avb/avb_util.cpp
index 4505382..2288674 100644
--- a/fs_mgr/libfs_avb/avb_util.cpp
+++ b/fs_mgr/libfs_avb/avb_util.cpp
@@ -124,6 +124,64 @@
return true;
}
+std::unique_ptr<FsAvbHashDescriptor> GetHashDescriptor(
+ const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images) {
+ bool found = false;
+ const uint8_t* desc_partition_name;
+ auto hash_desc = std::make_unique<FsAvbHashDescriptor>();
+
+ for (const auto& vbmeta : vbmeta_images) {
+ size_t num_descriptors;
+ std::unique_ptr<const AvbDescriptor*[], decltype(&avb_free)> descriptors(
+ avb_descriptor_get_all(vbmeta.data(), vbmeta.size(), &num_descriptors), avb_free);
+
+ if (!descriptors || num_descriptors < 1) {
+ continue;
+ }
+
+ for (size_t n = 0; n < num_descriptors && !found; n++) {
+ AvbDescriptor desc;
+ if (!avb_descriptor_validate_and_byteswap(descriptors[n], &desc)) {
+ LWARNING << "Descriptor[" << n << "] is invalid";
+ continue;
+ }
+ if (desc.tag == AVB_DESCRIPTOR_TAG_HASH) {
+ desc_partition_name = (const uint8_t*)descriptors[n] + sizeof(AvbHashDescriptor);
+ if (!avb_hash_descriptor_validate_and_byteswap((AvbHashDescriptor*)descriptors[n],
+ hash_desc.get())) {
+ continue;
+ }
+ if (hash_desc->partition_name_len != partition_name.length()) {
+ continue;
+ }
+ // Notes that desc_partition_name is not NUL-terminated.
+ std::string hash_partition_name((const char*)desc_partition_name,
+ hash_desc->partition_name_len);
+ if (hash_partition_name == partition_name) {
+ found = true;
+ }
+ }
+ }
+
+ if (found) break;
+ }
+
+ if (!found) {
+ LERROR << "Hash descriptor not found: " << partition_name;
+ return nullptr;
+ }
+
+ hash_desc->partition_name = partition_name;
+
+ const uint8_t* desc_salt = desc_partition_name + hash_desc->partition_name_len;
+ hash_desc->salt = BytesToHex(desc_salt, hash_desc->salt_len);
+
+ const uint8_t* desc_digest = desc_salt + hash_desc->salt_len;
+ hash_desc->digest = BytesToHex(desc_digest, hash_desc->digest_len);
+
+ return hash_desc;
+}
+
std::unique_ptr<FsAvbHashtreeDescriptor> GetHashtreeDescriptor(
const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images) {
bool found = false;
diff --git a/fs_mgr/libfs_avb/avb_util.h b/fs_mgr/libfs_avb/avb_util.h
index 09c786a..e8f7c39 100644
--- a/fs_mgr/libfs_avb/avb_util.h
+++ b/fs_mgr/libfs_avb/avb_util.h
@@ -40,6 +40,9 @@
std::string GetAvbPropertyDescriptor(const std::string& key,
const std::vector<VBMetaData>& vbmeta_images);
+std::unique_ptr<FsAvbHashDescriptor> GetHashDescriptor(
+ const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images);
+
// AvbHashtreeDescriptor to dm-verity table setup.
std::unique_ptr<FsAvbHashtreeDescriptor> GetHashtreeDescriptor(
const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images);
diff --git a/fs_mgr/libfs_avb/fs_avb_util.cpp b/fs_mgr/libfs_avb/fs_avb_util.cpp
index f82f83d..1c14cc0 100644
--- a/fs_mgr/libfs_avb/fs_avb_util.cpp
+++ b/fs_mgr/libfs_avb/fs_avb_util.cpp
@@ -74,5 +74,15 @@
return GetHashtreeDescriptor(avb_partition_name, vbmeta_images);
}
+// Given a path, loads and verifies the vbmeta, to extract the Avb Hash descriptor.
+std::unique_ptr<FsAvbHashDescriptor> GetHashDescriptor(const std::string& avb_partition_name,
+ VBMetaData&& vbmeta) {
+ if (!vbmeta.size()) return nullptr;
+
+ std::vector<VBMetaData> vbmeta_images;
+ vbmeta_images.emplace_back(std::move(vbmeta));
+ return GetHashDescriptor(avb_partition_name, vbmeta_images);
+}
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/libfs_avb/include/fs_avb/fs_avb_util.h b/fs_mgr/libfs_avb/include/fs_avb/fs_avb_util.h
index ec8badb..3f37bd7 100644
--- a/fs_mgr/libfs_avb/include/fs_avb/fs_avb_util.h
+++ b/fs_mgr/libfs_avb/include/fs_avb/fs_avb_util.h
@@ -32,9 +32,20 @@
std::string* out_avb_partition_name,
VBMetaVerifyResult* out_verify_result);
+// Loads the single vbmeta from a given path.
+std::unique_ptr<VBMetaData> LoadAndVerifyVbmetaByPath(
+ const std::string& image_path, const std::string& partition_name,
+ const std::string& expected_public_key_blob, bool allow_verification_error,
+ bool rollback_protection, bool is_chained_vbmeta, std::string* out_public_key_data,
+ bool* out_verification_disabled, VBMetaVerifyResult* out_verify_result);
+
// Gets the hashtree descriptor for avb_partition_name from the vbmeta.
std::unique_ptr<FsAvbHashtreeDescriptor> GetHashtreeDescriptor(
const std::string& avb_partition_name, VBMetaData&& vbmeta);
+// Gets the hash descriptor for avb_partition_name from the vbmeta.
+std::unique_ptr<FsAvbHashDescriptor> GetHashDescriptor(const std::string& avb_partition_name,
+ VBMetaData&& vbmeta);
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/libfs_avb/include/fs_avb/types.h b/fs_mgr/libfs_avb/include/fs_avb/types.h
index bd638e6..f2aa7cc 100644
--- a/fs_mgr/libfs_avb/include/fs_avb/types.h
+++ b/fs_mgr/libfs_avb/include/fs_avb/types.h
@@ -55,6 +55,12 @@
std::ostream& operator<<(std::ostream& os, AvbHandleStatus status);
+struct FsAvbHashDescriptor : AvbHashDescriptor {
+ std::string partition_name;
+ std::string salt;
+ std::string digest;
+};
+
struct FsAvbHashtreeDescriptor : AvbHashtreeDescriptor {
std::string partition_name;
std::string salt;
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index ea0fca8..a779a78 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -97,7 +97,7 @@
cc_test {
name: "vts_core_liblp_test",
defaults: ["liblp_test_defaults"],
- test_suites: ["vts-core"],
+ test_suites: ["vts"],
auto_gen_config: true,
test_min_api_level: 29,
require_root: true,
@@ -108,3 +108,6 @@
defaults: ["liblp_test_defaults"],
}
+vts_config {
+ name: "VtsKernelLiblpTest",
+}
diff --git a/fs_mgr/liblp/Android.mk b/fs_mgr/liblp/Android.mk
deleted file mode 100644
index 7f7f891..0000000
--- a/fs_mgr/liblp/Android.mk
+++ /dev/null
@@ -1,22 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := VtsKernelLiblpTest
--include test/vts/tools/build/Android.host_config.mk
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index d496466..c37d70e 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -19,6 +19,7 @@
#include <string.h>
#include <algorithm>
+#include <limits>
#include <android-base/unique_fd.h>
@@ -40,6 +41,20 @@
return true;
}
+bool LinearExtent::OverlapsWith(const LinearExtent& other) const {
+ if (device_index_ != other.device_index()) {
+ return false;
+ }
+ return physical_sector() < other.end_sector() && other.physical_sector() < end_sector();
+}
+
+bool LinearExtent::OverlapsWith(const Interval& interval) const {
+ if (device_index_ != interval.device_index) {
+ return false;
+ }
+ return physical_sector() < interval.end && interval.start < end_sector();
+}
+
Interval LinearExtent::AsInterval() const {
return Interval(device_index(), physical_sector(), end_sector());
}
@@ -355,7 +370,10 @@
}
// Align the metadata size up to the nearest sector.
- metadata_max_size = AlignTo(metadata_max_size, LP_SECTOR_SIZE);
+ if (!AlignTo(metadata_max_size, LP_SECTOR_SIZE, &metadata_max_size)) {
+ LERROR << "Max metadata size " << metadata_max_size << " is too large.";
+ return false;
+ }
// Validate and build the block device list.
uint32_t logical_block_size = 0;
@@ -387,10 +405,15 @@
// 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);
+ bool ok;
+ if (out.alignment) {
+ ok = AlignTo(free_area_start, out.alignment, &free_area_start);
} else {
- free_area_start = AlignTo(free_area_start, logical_block_size);
+ ok = AlignTo(free_area_start, logical_block_size, &free_area_start);
+ }
+ if (!ok) {
+ LERROR << "Integer overflow computing free area start";
+ return false;
}
out.first_logical_sector = free_area_start / LP_SECTOR_SIZE;
@@ -427,10 +450,15 @@
// Compute the first free sector, factoring in alignment.
uint64_t free_area_start = total_reserved;
+ bool ok;
if (super.alignment || super.alignment_offset) {
- free_area_start = AlignTo(free_area_start, super.alignment, super.alignment_offset);
+ ok = AlignTo(free_area_start, super.alignment, &free_area_start);
} else {
- free_area_start = AlignTo(free_area_start, logical_block_size);
+ ok = AlignTo(free_area_start, logical_block_size, &free_area_start);
+ }
+ if (!ok) {
+ LERROR << "Integer overflow computing free area start";
+ return false;
}
super.first_logical_sector = free_area_start / LP_SECTOR_SIZE;
@@ -530,7 +558,11 @@
const Interval& current = extents[i];
DCHECK(previous.device_index == current.device_index);
- uint64_t aligned = AlignSector(block_devices_[current.device_index], previous.end);
+ uint64_t aligned;
+ if (!AlignSector(block_devices_[current.device_index], previous.end, &aligned)) {
+ LERROR << "Sector " << previous.end << " caused integer overflow.";
+ continue;
+ }
if (aligned >= current.start) {
// There is no gap between these two extents, try the next one.
// Note that we check with >= instead of >, since alignment may
@@ -716,7 +748,10 @@
// Choose an aligned sector for the midpoint. This could lead to one half
// being slightly larger than the other, but this will not restrict the
// size of partitions (it might lead to one extra extent if "B" overflows).
- midpoint = AlignSector(super, midpoint);
+ if (!AlignSector(super, midpoint, &midpoint)) {
+ LERROR << "Unexpected integer overflow aligning midpoint " << midpoint;
+ return free_list;
+ }
std::vector<Interval> first_half;
std::vector<Interval> second_half;
@@ -754,7 +789,11 @@
// If the sector ends where the next aligned chunk begins, then there's
// no missing gap to try and allocate.
const auto& block_device = block_devices_[extent->device_index()];
- uint64_t next_aligned_sector = AlignSector(block_device, extent->end_sector());
+ uint64_t next_aligned_sector;
+ if (!AlignSector(block_device, extent->end_sector(), &next_aligned_sector)) {
+ LERROR << "Integer overflow aligning sector " << extent->end_sector();
+ return nullptr;
+ }
if (extent->end_sector() == next_aligned_sector) {
return nullptr;
}
@@ -774,8 +813,7 @@
bool MetadataBuilder::IsAnyRegionCovered(const std::vector<Interval>& regions,
const LinearExtent& candidate) const {
for (const auto& region : regions) {
- if (region.device_index == candidate.device_index() &&
- (candidate.OwnsSector(region.start) || candidate.OwnsSector(region.end))) {
+ if (candidate.OverlapsWith(region)) {
return true;
}
}
@@ -786,11 +824,10 @@
for (const auto& partition : partitions_) {
for (const auto& extent : partition->extents()) {
LinearExtent* linear = extent->AsLinearExtent();
- if (!linear || linear->device_index() != candidate.device_index()) {
+ if (!linear) {
continue;
}
- if (linear->OwnsSector(candidate.physical_sector()) ||
- linear->OwnsSector(candidate.end_sector() - 1)) {
+ if (linear->OverlapsWith(candidate)) {
return true;
}
}
@@ -913,13 +950,19 @@
return size;
}
-uint64_t MetadataBuilder::AlignSector(const LpMetadataBlockDevice& block_device,
- uint64_t sector) const {
+bool MetadataBuilder::AlignSector(const LpMetadataBlockDevice& block_device, uint64_t sector,
+ uint64_t* out) const {
// Note: when reading alignment info from the Kernel, we don't assume it
// is aligned to the sector size, so we round up to the nearest sector.
uint64_t lba = sector * LP_SECTOR_SIZE;
- uint64_t aligned = AlignTo(lba, block_device.alignment, block_device.alignment_offset);
- return AlignTo(aligned, LP_SECTOR_SIZE) / LP_SECTOR_SIZE;
+ if (!AlignTo(lba, block_device.alignment, out)) {
+ return false;
+ }
+ if (!AlignTo(*out, LP_SECTOR_SIZE, out)) {
+ return false;
+ }
+ *out /= LP_SECTOR_SIZE;
+ return true;
}
bool MetadataBuilder::FindBlockDeviceByName(const std::string& partition_name,
@@ -993,7 +1036,12 @@
bool MetadataBuilder::ResizePartition(Partition* partition, uint64_t requested_size,
const std::vector<Interval>& free_region_hint) {
// Align the space needed up to the nearest sector.
- uint64_t aligned_size = AlignTo(requested_size, geometry_.logical_block_size);
+ uint64_t aligned_size;
+ if (!AlignTo(requested_size, geometry_.logical_block_size, &aligned_size)) {
+ LERROR << "Cannot resize partition " << partition->name() << " to " << requested_size
+ << " bytes; integer overflow.";
+ return false;
+ }
uint64_t old_size = partition->size();
if (!ValidatePartitionSizeChange(partition, old_size, aligned_size, false)) {
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index ca8df61..1a3250a 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) {
@@ -228,13 +228,14 @@
ASSERT_EQ(extent.target_type, LP_TARGET_TYPE_LINEAR);
EXPECT_EQ(extent.num_sectors, 80);
+ uint64_t aligned_lba;
uint64_t lba = extent.target_data * LP_SECTOR_SIZE;
- uint64_t aligned_lba = AlignTo(lba, device_info.alignment, device_info.alignment_offset);
+ ASSERT_TRUE(AlignTo(lba, device_info.alignment, &aligned_lba));
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 +653,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 +676,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);
}
@@ -937,3 +938,131 @@
EXPECT_EQ(exported->header.header_size, sizeof(LpMetadataHeaderV1_2));
EXPECT_EQ(exported->header.flags, 0x5e5e5e5e);
}
+
+static Interval ToInterval(const std::unique_ptr<Extent>& extent) {
+ if (LinearExtent* le = extent->AsLinearExtent()) {
+ return le->AsInterval();
+ }
+ return {0, 0, 0};
+}
+
+static void AddPartition(const std::unique_ptr<MetadataBuilder>& builder,
+ const std::string& partition_name, uint64_t num_sectors,
+ uint64_t start_sector, std::vector<Interval>* intervals) {
+ Partition* p = builder->AddPartition(partition_name, "group", 0);
+ ASSERT_NE(p, nullptr);
+ ASSERT_TRUE(builder->AddLinearExtent(p, "super", num_sectors, start_sector));
+ ASSERT_EQ(p->extents().size(), 1);
+
+ if (!intervals) {
+ return;
+ }
+
+ auto new_interval = ToInterval(p->extents().back());
+ std::vector<Interval> new_intervals = {new_interval};
+
+ auto overlap = Interval::Intersect(*intervals, new_intervals);
+ ASSERT_TRUE(overlap.empty());
+
+ intervals->push_back(new_interval);
+}
+
+TEST_F(BuilderTest, CollidedExtents) {
+ BlockDeviceInfo super("super", 8_GiB, 786432, 229376, 4096);
+ std::vector<BlockDeviceInfo> block_devices = {super};
+
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(block_devices, "super", 65536, 2);
+ ASSERT_NE(builder, nullptr);
+
+ ASSERT_TRUE(builder->AddGroup("group", 0));
+
+ std::vector<Interval> old_intervals;
+ AddPartition(builder, "system", 10229008, 2048, &old_intervals);
+ AddPartition(builder, "test_a", 648, 12709888, &old_intervals);
+ AddPartition(builder, "test_b", 625184, 12711936, &old_intervals);
+ AddPartition(builder, "test_c", 130912, 13338624, &old_intervals);
+ AddPartition(builder, "test_d", 888, 13469696, &old_intervals);
+ AddPartition(builder, "test_e", 888, 13471744, &old_intervals);
+ AddPartition(builder, "test_f", 888, 13475840, &old_intervals);
+ AddPartition(builder, "test_g", 888, 13477888, &old_intervals);
+
+ // Don't track the first vendor interval, since it will get extended.
+ AddPartition(builder, "vendor", 2477920, 10231808, nullptr);
+
+ std::vector<Interval> new_intervals;
+
+ Partition* p = builder->FindPartition("vendor");
+ ASSERT_NE(p, nullptr);
+ ASSERT_TRUE(builder->ResizePartition(p, 1282031616));
+ ASSERT_GE(p->extents().size(), 1);
+ for (const auto& extent : p->extents()) {
+ new_intervals.push_back(ToInterval(extent));
+ }
+
+ std::vector<Interval> overlap = Interval::Intersect(old_intervals, new_intervals);
+ ASSERT_TRUE(overlap.empty());
+}
+
+TEST_F(BuilderTest, LinearExtentOverlap) {
+ LinearExtent extent(20, 0, 10);
+
+ EXPECT_TRUE(extent.OverlapsWith(LinearExtent{20, 0, 10}));
+ EXPECT_TRUE(extent.OverlapsWith(LinearExtent{50, 0, 10}));
+ EXPECT_FALSE(extent.OverlapsWith(LinearExtent{20, 0, 30}));
+ EXPECT_FALSE(extent.OverlapsWith(LinearExtent{10, 0, 0}));
+ EXPECT_TRUE(extent.OverlapsWith(LinearExtent{20, 0, 0}));
+ EXPECT_TRUE(extent.OverlapsWith(LinearExtent{40, 0, 0}));
+ EXPECT_TRUE(extent.OverlapsWith(LinearExtent{20, 0, 15}));
+
+ EXPECT_FALSE(extent.OverlapsWith(LinearExtent{20, 1, 0}));
+ EXPECT_FALSE(extent.OverlapsWith(LinearExtent{50, 1, 10}));
+ EXPECT_FALSE(extent.OverlapsWith(LinearExtent{40, 1, 0}));
+ 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);
+}
+
+TEST_F(BuilderTest, ResizeOverflow) {
+ BlockDeviceInfo super("super", 8_GiB, 786432, 229376, 4096);
+ std::vector<BlockDeviceInfo> block_devices = {super};
+
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(block_devices, "super", 65536, 2);
+ ASSERT_NE(builder, nullptr);
+
+ ASSERT_TRUE(builder->AddGroup("group", 0));
+
+ Partition* p = builder->AddPartition("system", "default", 0);
+ ASSERT_NE(p, nullptr);
+ ASSERT_FALSE(builder->ResizePartition(p, 18446744073709551615ULL));
+}
diff --git a/fs_mgr/liblp/device_test.cpp b/fs_mgr/liblp/device_test.cpp
index 382d53d..6af9d94 100644
--- a/fs_mgr/liblp/device_test.cpp
+++ b/fs_mgr/liblp/device_test.cpp
@@ -50,12 +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);
}
TEST_F(DeviceTest, ReadSuperPartitionCurrentSlot) {
diff --git a/fs_mgr/liblp/images.cpp b/fs_mgr/liblp/images.cpp
index e4d92ca..0b1e522 100644
--- a/fs_mgr/liblp/images.cpp
+++ b/fs_mgr/liblp/images.cpp
@@ -124,7 +124,7 @@
}
bool WriteToImageFile(const std::string& file, const LpMetadata& input) {
- unique_fd fd(open(file.c_str(), O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC, 0644));
+ unique_fd fd(open(file.c_str(), O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC | O_BINARY, 0644));
if (fd < 0) {
PERROR << __PRETTY_FUNCTION__ << " open failed: " << file;
return false;
@@ -184,7 +184,7 @@
}
bool ImageBuilder::Export(const std::string& file) {
- unique_fd fd(open(file.c_str(), O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC, 0644));
+ unique_fd fd(open(file.c_str(), O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC | O_BINARY, 0644));
if (fd < 0) {
PERROR << "open failed: " << file;
return false;
@@ -208,7 +208,7 @@
std::string file_name = "super_" + name + ".img";
std::string file_path = output_dir + "/" + file_name;
- static const int kOpenFlags = O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC | O_NOFOLLOW;
+ static const int kOpenFlags = O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC | O_NOFOLLOW | O_BINARY;
unique_fd fd(open(file_path.c_str(), kOpenFlags, 0644));
if (fd < 0) {
PERROR << "open failed: " << file_path;
@@ -443,7 +443,7 @@
}
int ImageBuilder::OpenImageFile(const std::string& file) {
- android::base::unique_fd source_fd = GetControlFileOrOpen(file.c_str(), O_RDONLY | O_CLOEXEC);
+ unique_fd source_fd = GetControlFileOrOpen(file.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY);
if (source_fd < 0) {
PERROR << "open image file failed: " << file;
return -1;
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index f7738fb..89a47b1 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -71,9 +71,8 @@
uint64_t end_sector() const { return physical_sector_ + num_sectors_; }
uint32_t device_index() const { return device_index_; }
- bool OwnsSector(uint64_t sector) const {
- return sector >= physical_sector_ && sector < end_sector();
- }
+ bool OverlapsWith(const LinearExtent& other) const;
+ bool OverlapsWith(const Interval& interval) const;
Interval AsInterval() const;
@@ -145,7 +144,6 @@
std::vector<std::unique_ptr<Extent>> extents_;
uint32_t attributes_;
uint64_t size_;
- bool disabled_;
};
// An interval in the metadata. This is similar to a LinearExtent with one difference.
@@ -322,9 +320,6 @@
// Set the LP_HEADER_FLAG_VIRTUAL_AB_DEVICE flag.
void SetVirtualABDeviceFlag();
- // If set, checks for slot suffixes will be ignored internally.
- void IgnoreSlotSuffixing();
-
bool GetBlockDeviceInfo(const std::string& partition_name, BlockDeviceInfo* info) const;
bool UpdateBlockDeviceInfo(const std::string& partition_name, const BlockDeviceInfo& info);
@@ -360,7 +355,7 @@
bool GrowPartition(Partition* partition, uint64_t aligned_size,
const std::vector<Interval>& free_region_hint);
void ShrinkPartition(Partition* partition, uint64_t aligned_size);
- uint64_t AlignSector(const LpMetadataBlockDevice& device, uint64_t sector) const;
+ bool AlignSector(const LpMetadataBlockDevice& device, uint64_t sector, uint64_t* out) const;
uint64_t TotalSizeOfGroup(PartitionGroup* group) const;
bool UpdateBlockDeviceInfo(size_t index, const BlockDeviceInfo& info);
bool FindBlockDeviceByName(const std::string& partition_name, uint32_t* index) const;
diff --git a/fs_mgr/liblp/liblp_test.xml b/fs_mgr/liblp/liblp_test.xml
index d9ee12e..98414b1 100644
--- a/fs_mgr/liblp/liblp_test.xml
+++ b/fs_mgr/liblp/liblp_test.xml
@@ -19,7 +19,6 @@
<option name="cleanup" value="true" />
<option name="push" value="liblp_test->/data/local/tmp/liblp_test" />
</target_preparer>
- <option name="test-suite-tag" value="vts-core" />
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
<option name="module-name" value="liblp_test" />
diff --git a/fs_mgr/liblp/utility.h b/fs_mgr/liblp/utility.h
index 0661769..c4fe3ed 100644
--- a/fs_mgr/liblp/utility.h
+++ b/fs_mgr/liblp/utility.h
@@ -21,6 +21,7 @@
#include <stdint.h>
#include <sys/types.h>
+#include <limits>
#include <string>
#include <string_view>
@@ -66,27 +67,26 @@
void SHA256(const void* data, size_t length, uint8_t out[32]);
// Align |base| such that it is evenly divisible by |alignment|, which does not
-// have to be a power of two.
-constexpr uint64_t AlignTo(uint64_t base, uint32_t alignment) {
+// have to be a power of two. Return false on overflow.
+template <typename T>
+bool AlignTo(T base, uint32_t alignment, T* out) {
+ static_assert(std::numeric_limits<T>::is_integer);
+ static_assert(!std::numeric_limits<T>::is_signed);
if (!alignment) {
- return base;
+ *out = base;
+ return true;
}
- uint64_t remainder = base % alignment;
+ T remainder = base % alignment;
if (remainder == 0) {
- return base;
+ *out = base;
+ return true;
}
- 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;
+ T to_add = alignment - remainder;
+ if (to_add > std::numeric_limits<T>::max() - base) {
+ return false;
}
- return aligned;
+ *out = base + to_add;
+ return true;
}
// Update names from C++ strings.
diff --git a/fs_mgr/liblp/utility_test.cpp b/fs_mgr/liblp/utility_test.cpp
index cac3989..fc90872 100644
--- a/fs_mgr/liblp/utility_test.cpp
+++ b/fs_mgr/liblp/utility_test.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <optional>
+
#include <gtest/gtest.h>
#include <liblp/builder.h>
#include <liblp/liblp.h>
@@ -58,15 +60,28 @@
EXPECT_EQ(GetBackupMetadataOffset(geometry, 0), backup_start + 16384 * 0);
}
+std::optional<uint64_t> AlignTo(uint64_t base, uint32_t alignment) {
+ uint64_t r;
+ if (!AlignTo(base, alignment, &r)) {
+ return {};
+ }
+ return {r};
+}
+
TEST(liblp, AlignTo) {
- EXPECT_EQ(AlignTo(37, 0), 37);
- EXPECT_EQ(AlignTo(1024, 1024), 1024);
- 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(37, 0), std::optional<uint64_t>(37));
+ EXPECT_EQ(AlignTo(1024, 1024), std::optional<uint64_t>(1024));
+ EXPECT_EQ(AlignTo(555, 1024), std::optional<uint64_t>(1024));
+ EXPECT_EQ(AlignTo(555, 1000), std::optional<uint64_t>(1000));
+ EXPECT_EQ(AlignTo(0, 1024), std::optional<uint64_t>(0));
+ EXPECT_EQ(AlignTo(54, 32), std::optional<uint64_t>(64));
+ EXPECT_EQ(AlignTo(32, 32), std::optional<uint64_t>(32));
+ EXPECT_EQ(AlignTo(17, 32), std::optional<uint64_t>(32));
+
+ auto u32limit = std::numeric_limits<uint32_t>::max();
+ auto u64limit = std::numeric_limits<uint64_t>::max();
+ EXPECT_EQ(AlignTo(u64limit - u32limit + 1, u32limit), std::optional<uint64_t>{u64limit});
+ EXPECT_EQ(AlignTo(std::numeric_limits<uint64_t>::max(), 2), std::optional<uint64_t>{});
}
TEST(liblp, GetPartitionSlotSuffix) {
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 262b179..95301ff 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -26,7 +26,6 @@
"libbase",
"libcutils",
"liblog",
- "liblp",
],
static_libs: [
"libdm",
@@ -73,6 +72,7 @@
"device_info.cpp",
"snapshot.cpp",
"snapshot_stats.cpp",
+ "snapshot_stub.cpp",
"snapshot_metadata_updater.cpp",
"partition_cow_creator.cpp",
"return.cpp",
@@ -96,20 +96,11 @@
static_libs: [
"libfs_mgr_binder"
],
-
- shared_libs: [
- // TODO(b/148818798): remove when parent bug is fixed
- "libutilscallstack",
- ],
- cflags: [
- "-g",
- "-O0",
- "-DLIBSNAPSHOT_USE_CALLSTACK",
- ],
}
cc_library_static {
name: "libsnapshot_init",
+ native_coverage : true,
defaults: ["libsnapshot_defaults"],
srcs: [":libsnapshot_sources"],
recovery_available: true,
@@ -170,32 +161,29 @@
"snapshot_test.cpp",
],
shared_libs: [
- "android.hardware.boot@1.0",
- "android.hardware.boot@1.1",
"libbinder",
"libcrypto",
"libhidlbase",
"libprotobuf-cpp-lite",
- "libsparse",
"libutils",
"libz",
-
- // TODO(b/148818798): remove when parent bug is fixed
- "libutilscallstack",
],
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",
],
test_suites: [
- "vts-core",
+ "vts",
"device-tests"
],
test_min_api_level: 29,
@@ -208,6 +196,12 @@
defaults: ["libsnapshot_test_defaults"],
}
+// For VTS 10
+vts_config {
+ name: "VtsLibsnapshotTest",
+ test_config: "VtsLibsnapshotTest.xml"
+}
+
cc_binary {
name: "snapshotctl",
srcs: [
@@ -216,6 +210,7 @@
static_libs: [
"libfstab",
"libsnapshot",
+ "update_metadata-protos",
],
shared_libs: [
"android.hardware.boot@1.0",
@@ -229,12 +224,89 @@
"liblog",
"liblp",
"libprotobuf-cpp-lite",
+ "libstatslog",
"libutils",
+ ],
+}
- // TODO(b/148818798): remove when parent bug is fixed.
- "libutilscallstack",
+cc_test {
+ name: "snapshot_power_test",
+ srcs: [
+ "power_test.cpp",
],
- init_rc: [
- "snapshotctl.rc",
+ static_libs: [
+ "libsnapshot",
+ "update_metadata-protos",
],
+ shared_libs: [
+ "libbase",
+ "libfs_mgr_binder",
+ "liblog",
+ ],
+ gtest: false,
+}
+
+cc_defaults {
+ name: "libsnapshot_fuzzer_defaults",
+ native_coverage : true,
+ srcs: [
+ // Compile the protobuf definition again with type full.
+ "android/snapshot/snapshot_fuzz.proto",
+ "update_engine/update_metadata.proto",
+ "fuzz_utils.cpp",
+ "snapshot_fuzz.cpp",
+ "snapshot_fuzz_utils.cpp",
+
+ // Compile libsnapshot sources directly to avoid dependency
+ // to update_metadata-protos
+ ":libsnapshot_sources",
+ ],
+ static_libs: [
+ "libbase",
+ "libcrypto_static",
+ "libcutils",
+ "libext2_uuid",
+ "libext4_utils",
+ "libfstab",
+ "libfs_mgr",
+ "libgtest", // from libsnapshot_test_helpers
+ "libgmock", // from libsnapshot_test_helpers
+ "liblog",
+ "liblp",
+ "libsnapshot_test_helpers",
+ "libprotobuf-mutator",
+ ],
+ header_libs: [
+ "libfiemap_headers",
+ "libstorage_literals_headers",
+ ],
+ proto: {
+ type: "full",
+ canonical_path_from_root: false,
+ local_include_dirs: ["."],
+ },
+}
+
+cc_fuzz {
+ name: "libsnapshot_fuzzer",
+ defaults: ["libsnapshot_fuzzer_defaults"],
+ corpus: ["corpus/*"],
+ fuzz_config: {
+ cc: ["android-virtual-ab+bugs@google.com"],
+ componentid: 30545,
+ hotlists: ["1646452"],
+ fuzz_on_haiku_host: false,
+ fuzz_on_haiku_device: true,
+ },
+}
+
+cc_test {
+ name: "libsnapshot_fuzzer_test",
+ defaults: ["libsnapshot_fuzzer_defaults"],
+ data: ["corpus/*"],
+ test_suites: [
+ "device-tests",
+ ],
+ auto_gen_config: true,
+ require_root: true,
}
diff --git a/fs_mgr/libsnapshot/PowerTest.md b/fs_mgr/libsnapshot/PowerTest.md
new file mode 100644
index 0000000..0b0cb5d
--- /dev/null
+++ b/fs_mgr/libsnapshot/PowerTest.md
@@ -0,0 +1,40 @@
+snapshot\_power\_test
+---------------------
+
+snapshot\_power\_test is a standalone test to simulate power failures during a snapshot-merge operation.
+
+### Test Setup
+
+Start by creating two large files that will be used as the pre-merge and post-merge state. You can take two different partition images (for example, a product.img from two separate builds), or just create random data:
+
+ dd if=/dev/urandom of=pre-merge count=1024 bs=1048576
+ dd if=/dev/urandom of=post-merge count=1024 bs=1048576
+
+Next, push these files to an unencrypted directory on the device:
+
+ adb push pre-merge /data/local/unencrypted
+ adb push post-merge /data/local/unencrypted
+
+Next, run the test setup:
+
+ adb sync data
+ adb shell /data/nativetest64/snapshot_power_test/snapshot_power_test \
+ /data/local/unencrypted/pre-merge \
+ /data/local/unencrypted/post-merge
+
+This will create the necessary fiemap-based images.
+
+### Running
+The actual test can be run via `run_power_test.sh`. Its syntax is:
+
+ run_power_test.sh <POST_MERGE_FILE>
+
+`POST_MERGE_FILE` should be the path on the device of the image to validate the merge against. Example:
+
+ run_power_test.sh /data/local/unencrypted/post-merge
+
+The device will begin the merge with a 5% chance of injecting a kernel crash every 10ms. The device should be capable of rebooting normally without user intervention. Once the merge has completed, the test will run a final check command to validate the contents of the snapshot against the post-merge file. It will error if there are any incorrect blocks.
+
+Two environment variables can be passed to `run_power_test.sh`:
+1. `FAIL_RATE` - A fraction between 0 and 100 (inclusive) indicating the probability the device should inject a kernel crash every 10ms.
+2. `DEVICE_SERIAL` - If multiple devices are attached to adb, this argument is passed as the serial to select (to `adb -s`).
diff --git a/fs_mgr/libsnapshot/VtsLibsnapshotTest.xml b/fs_mgr/libsnapshot/VtsLibsnapshotTest.xml
new file mode 100644
index 0000000..b53b51e
--- /dev/null
+++ b/fs_mgr/libsnapshot/VtsLibsnapshotTest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for VTS VtsLibsnapshotTest">
+ <option name="config-descriptor:metadata" key="plan" value="vts-kernel"/>
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher">
+ <option name="abort-on-push-failure" value="false"/>
+ <option name="push-group" value="HostDrivenTest.push"/>
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
+ <option name="test-module-name" value="VtsLibsnapshotTest"/>
+ <option name="binary-test-source" value="_32bit::DATA/nativetest/vts_libsnapshot_test/vts_libsnapshot_test"/>
+ <option name="binary-test-source" value="_64bit::DATA/nativetest64/vts_libsnapshot_test/vts_libsnapshot_test"/>
+ <option name="binary-test-type" value="gtest"/>
+ <option name="test-timeout" value="5m"/>
+ </test>
+</configuration>
diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
index 2ac0c44..0328132 100644
--- a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
+++ b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
@@ -132,7 +132,7 @@
uint64 metadata_sectors = 4;
}
-// Next: 2
+// Next: 4
message SnapshotMergeReport {
// Status of the update after the merge attempts.
UpdateState state = 1;
@@ -140,4 +140,7 @@
// Number of reboots that occurred after issuing and before completeing the
// merge of all the snapshot devices.
int32 resume_count = 2;
+
+ // Total size of all the COW images before the update.
+ uint64 cow_file_size = 3;
}
diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto
new file mode 100644
index 0000000..a55b42a
--- /dev/null
+++ b/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto
@@ -0,0 +1,110 @@
+// 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.
+
+syntax = "proto3";
+package android.snapshot;
+
+import "update_engine/update_metadata.proto";
+
+// Controls the behavior of IDeviceInfo.
+// Next: 6
+message FuzzDeviceInfoData {
+ bool slot_suffix_is_a = 1;
+ bool is_overlayfs_setup = 2;
+ bool allow_set_boot_control_merge_status = 3;
+ bool allow_set_slot_as_unbootable = 4;
+ bool is_recovery = 5;
+}
+
+// Controls the behavior of the test SnapshotManager.
+// Next: 2
+message FuzzSnapshotManagerData {
+ bool is_local_image_manager = 1;
+}
+
+// A simplified version of CreateLogicalPartitionParams for fuzzing.
+// Next: 9
+message CreateLogicalPartitionParamsProto {
+ bool use_correct_super = 1;
+ string block_device = 2;
+ bool has_metadata_slot = 3;
+ uint32 metadata_slot = 4;
+ string partition_name = 5;
+ bool force_writable = 6;
+ int64 timeout_millis = 7;
+ string device_name = 8;
+}
+
+// Mimics the API of ISnapshotManager. Defines one action on the snapshot
+// manager.
+// Next: 18
+message SnapshotManagerActionProto {
+ message NoArgs {}
+ message ProcessUpdateStateArgs {
+ bool has_before_cancel = 1;
+ bool fail_before_cancel = 2;
+ }
+ message CreateLogicalAndSnapshotPartitionsArgs {
+ bool use_correct_super = 1;
+ string super = 2;
+ int64 timeout_millis = 3;
+ }
+ message RecoveryCreateSnapshotDevicesArgs {
+ bool has_metadata_device_object = 1;
+ bool metadata_mounted = 2;
+ }
+ reserved 18 to 9999;
+ oneof value {
+ NoArgs begin_update = 1;
+ NoArgs cancel_update = 2;
+ bool finished_snapshot_writes = 3;
+ NoArgs initiate_merge = 4;
+ ProcessUpdateStateArgs process_update_state = 5;
+ bool get_update_state = 6;
+ chromeos_update_engine.DeltaArchiveManifest create_update_snapshots = 7;
+ CreateLogicalPartitionParamsProto map_update_snapshot = 8;
+ string unmap_update_snapshot = 9;
+ NoArgs need_snapshots_in_first_stage_mount = 10;
+ CreateLogicalAndSnapshotPartitionsArgs create_logical_and_snapshot_partitions = 11;
+ bool handle_imminent_data_wipe = 12;
+ NoArgs recovery_create_snapshot_devices = 13;
+ RecoveryCreateSnapshotDevicesArgs recovery_create_snapshot_devices_with_metadata = 14;
+ NoArgs dump = 15;
+ NoArgs ensure_metadata_mounted = 16;
+ NoArgs get_snapshot_merge_stats_instance = 17;
+
+ // Test directives that has nothing to do with ISnapshotManager API surface.
+ NoArgs switch_slot = 10000;
+ }
+}
+
+// Includes all data that needs to be fuzzed.
+message SnapshotFuzzData {
+ FuzzDeviceInfoData device_info_data = 1;
+ FuzzSnapshotManagerData manager_data = 2;
+
+ // If true:
+ // - if super_data is empty, create empty super partition metadata.
+ // - otherwise, create super partition metadata accordingly.
+ // If false, no valid super partition metadata (it is zeroed)
+ bool is_super_metadata_valid = 3;
+ chromeos_update_engine.DeltaArchiveManifest super_data = 4;
+
+ // Whether the directory that mocks /metadata/ota/snapshot is created.
+ bool has_metadata_snapshots_dir = 5;
+
+ // More data used to prep the test before running actions.
+ reserved 6 to 9999;
+ repeated SnapshotManagerActionProto actions = 10000;
+}
diff --git a/fs_mgr/libsnapshot/corpus/launch_device.txt b/fs_mgr/libsnapshot/corpus/launch_device.txt
new file mode 100644
index 0000000..55a7f2c
--- /dev/null
+++ b/fs_mgr/libsnapshot/corpus/launch_device.txt
@@ -0,0 +1,161 @@
+device_info_data {
+ slot_suffix_is_a: true
+ is_overlayfs_setup: false
+ allow_set_boot_control_merge_status: true
+ allow_set_slot_as_unbootable: true
+ is_recovery: false
+}
+manager_data {
+ is_local_image_manager: false
+}
+is_super_metadata_valid: true
+super_data {
+ partitions {
+ partition_name: "sys_a"
+ new_partition_info {
+ size: 3145728
+ }
+ }
+ partitions {
+ partition_name: "vnd_a"
+ new_partition_info {
+ size: 3145728
+ }
+ }
+ partitions {
+ partition_name: "prd_a"
+ new_partition_info {
+ size: 3145728
+ }
+ }
+ dynamic_partition_metadata {
+ groups {
+ name: "group_google_dp_a"
+ size: 15728640
+ partition_names: "sys_a"
+ partition_names: "vnd_a"
+ partition_names: "prd_a"
+ }
+ }
+}
+has_metadata_snapshots_dir: true
+actions {
+ begin_update {
+ }
+}
+actions {
+ create_update_snapshots {
+ partitions {
+ partition_name: "sys"
+ new_partition_info {
+ size: 3878912
+ }
+ operations {
+ type: ZERO,
+ dst_extents {
+ start_block: 0
+ num_blocks: 947
+ }
+ }
+ }
+ partitions {
+ partition_name: "vnd"
+ new_partition_info {
+ size: 3878912
+ }
+ operations {
+ type: ZERO,
+ dst_extents {
+ start_block: 0
+ num_blocks: 947
+ }
+ }
+ }
+ partitions {
+ partition_name: "prd"
+ new_partition_info {
+ size: 3878912
+ }
+ operations {
+ type: ZERO,
+ dst_extents {
+ start_block: 0
+ num_blocks: 947
+ }
+ }
+ }
+ dynamic_partition_metadata {
+ groups {
+ name: "group_google_dp"
+ size: 15728640
+ partition_names: "sys"
+ partition_names: "vnd"
+ partition_names: "prd"
+ }
+ }
+ }
+}
+actions {
+ map_update_snapshot {
+ use_correct_super: true
+ has_metadata_slot: true
+ metadata_slot: 1
+ partition_name: "sys_b"
+ force_writable: true
+ timeout_millis: 3000
+ }
+}
+actions {
+ map_update_snapshot {
+ use_correct_super: true
+ has_metadata_slot: true
+ metadata_slot: 1
+ partition_name: "vnd_b"
+ force_writable: true
+ timeout_millis: 3000
+ }
+}
+actions {
+ map_update_snapshot {
+ use_correct_super: true
+ has_metadata_slot: true
+ metadata_slot: 1
+ partition_name: "prd_b"
+ force_writable: true
+ timeout_millis: 3000
+ }
+}
+actions {
+ finished_snapshot_writes: false
+}
+actions {
+ unmap_update_snapshot: "sys_b"
+}
+actions {
+ unmap_update_snapshot: "vnd_b"
+}
+actions {
+ unmap_update_snapshot: "prd_b"
+}
+actions {
+ switch_slot {
+ }
+}
+actions {
+ need_snapshots_in_first_stage_mount {
+ }
+}
+actions {
+ create_logical_and_snapshot_partitions {
+ use_correct_super: true
+ timeout_millis: 5000
+ }
+}
+actions {
+ initiate_merge {
+ }
+}
+actions {
+ process_update_state {
+ }
+}
diff --git a/fs_mgr/libsnapshot/fuzz.sh b/fs_mgr/libsnapshot/fuzz.sh
new file mode 100755
index 0000000..0e57674
--- /dev/null
+++ b/fs_mgr/libsnapshot/fuzz.sh
@@ -0,0 +1,90 @@
+#!/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_INIT_CORPUS_DIR=/data/fuzz/${TARGET_ARCH}/${FUZZ_TARGET}/corpus
+DEVICE_GENERATED_CORPUS_DIR=/data/local/tmp/${FUZZ_TARGET}/corpus
+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)
+ NATIVE_COVERAGE="true" NATIVE_LINE_COVERAGE="true" COVERAGE_PATHS="${PROJECT_PATH}" m ${FUZZ_TARGET}
+ ret=$?
+ popd
+ return ${ret}
+}
+
+prepare_device() {
+ adb root && adb remount &&
+ adb shell mkdir -p ${DEVICE_GENERATED_CORPUS_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} &&
+ adb push ${ANDROID_PRODUCT_OUT}/${DEVICE_INIT_CORPUS_DIR} $(dirname ${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_corpus() {
+ [[ "$@" ]] || { echo "run with -runs=X"; return 1; }
+
+ prepare_device &&
+ build_normal &&
+ push_binary &&
+ adb shell ${FUZZ_BINARY} "$@" ${DEVICE_INIT_CORPUS_DIR} ${DEVICE_GENERATED_CORPUS_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_INIT_CORPUS_DIR} ${DEVICE_GENERATED_CORPUS_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_corpus "$@" &&
+ 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..0263f7e
--- /dev/null
+++ b/fs_mgr/libsnapshot/fuzz_utils.cpp
@@ -0,0 +1,38 @@
+// 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;
+}
+
+const google::protobuf::OneofDescriptor* GetProtoValueDescriptor(
+ const google::protobuf::Descriptor* action_desc) {
+ CHECK(action_desc);
+ CHECK(action_desc->oneof_decl_count() == 1)
+ << action_desc->oneof_decl_count() << " oneof fields found in " << action_desc->name()
+ << "; only one is expected.";
+ auto* oneof_value_desc = action_desc->oneof_decl(0);
+ CHECK(oneof_value_desc);
+ CHECK(oneof_value_desc->name() == "value")
+ << "oneof field has name " << oneof_value_desc->name();
+ return oneof_value_desc;
+}
+
+} // 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..20b13b2
--- /dev/null
+++ b/fs_mgr/libsnapshot/fuzz_utils.h
@@ -0,0 +1,285 @@
+// 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 <map>
+#include <string>
+#include <string_view>
+
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/message.h>
+#include <google/protobuf/repeated_field.h>
+
+// Utilities for using a protobuf definition to fuzz APIs in a class.
+// Terms:
+// The "fuzzed class" is the C++ class definition whose functions are fuzzed.
+// The "fuzzed object" is an instantiated object of the fuzzed class. It is
+// typically created and destroyed for each test run.
+// An "action" is an operation on the fuzzed object that may mutate its state.
+// This typically involves one function call into the fuzzed object.
+
+namespace android::fuzz {
+
+// CHECK(value) << msg
+void CheckInternal(bool value, std::string_view msg);
+
+// Get the oneof descriptor inside Action
+const google::protobuf::OneofDescriptor* GetProtoValueDescriptor(
+ const google::protobuf::Descriptor* action_desc);
+
+template <typename Class>
+using FunctionMapImpl =
+ std::map<int, std::function<void(Class*, const google::protobuf::Message& action_proto,
+ const google::protobuf::FieldDescriptor* field_desc)>>;
+
+template <typename Class>
+class FunctionMap : public FunctionMapImpl<Class> {
+ public:
+ void CheckEmplace(typename FunctionMapImpl<Class>::key_type key,
+ typename FunctionMapImpl<Class>::mapped_type&& value) {
+ auto [it, inserted] = this->emplace(key, std::move(value));
+ CheckInternal(inserted,
+ "Multiple implementation registered for tag number " + std::to_string(key));
+ }
+};
+
+template <typename Action>
+int CheckConsistency() {
+ const auto* function_map = Action::GetFunctionMap();
+ const auto* action_value_desc = GetProtoValueDescriptor(Action::Proto::GetDescriptor());
+
+ for (int field_index = 0; field_index < action_value_desc->field_count(); ++field_index) {
+ const auto* field_desc = action_value_desc->field(field_index);
+ CheckInternal(function_map->find(field_desc->number()) != function_map->end(),
+ "Missing impl for function " + field_desc->camelcase_name());
+ }
+ return 0;
+}
+
+// Get the field descriptor for the oneof field in the action message. If no oneof field is set,
+// return nullptr.
+template <typename Action>
+const google::protobuf::FieldDescriptor* GetValueFieldDescriptor(
+ const typename Action::Proto& action_proto) {
+ static auto* action_value_desc = GetProtoValueDescriptor(Action::Proto::GetDescriptor());
+
+ auto* action_refl = Action::Proto::GetReflection();
+ if (!action_refl->HasOneof(action_proto, action_value_desc)) {
+ return nullptr;
+ }
+ return action_refl->GetOneofFieldDescriptor(action_proto, action_value_desc);
+}
+
+template <typename Action>
+void ExecuteActionProto(typename Action::ClassType* module,
+ const typename Action::Proto& action_proto) {
+ const auto* field_desc = GetValueFieldDescriptor<Action>(action_proto);
+ if (field_desc == nullptr) return;
+ auto number = field_desc->number();
+ const auto& map = *Action::GetFunctionMap();
+ auto it = map.find(number);
+ CheckInternal(it != map.end(), "Missing impl for function " + field_desc->camelcase_name());
+ const auto& func = it->second;
+ func(module, action_proto, field_desc);
+}
+
+template <typename Action>
+void ExecuteAllActionProtos(
+ typename Action::ClassType* module,
+ const google::protobuf::RepeatedPtrField<typename Action::Proto>& action_protos) {
+ for (const auto& proto : action_protos) {
+ ExecuteActionProto<Action>(module, proto);
+ }
+}
+
+// Safely cast message to T. Returns a pointer to message if cast successfully, otherwise nullptr.
+template <typename T>
+const T* SafeCast(const google::protobuf::Message& message) {
+ if (message.GetDescriptor() != T::GetDescriptor()) {
+ return nullptr;
+ }
+ return static_cast<const T*>(&message);
+}
+
+// Cast message to const T&. Abort if type mismatch.
+template <typename T>
+const T& CheckedCast(const google::protobuf::Message& message) {
+ const auto* ptr = SafeCast<T>(message);
+ CheckInternal(ptr, "Cannot cast " + message.GetDescriptor()->name() + " to " +
+ T::GetDescriptor()->name());
+ return *ptr;
+}
+
+// A templated way to a primitive field from a message using reflection.
+template <typename T>
+struct PrimitiveGetter;
+#define FUZZ_DEFINE_PRIMITIVE_GETTER(type, func_name) \
+ template <> \
+ struct PrimitiveGetter<type> { \
+ static constexpr const auto fp = &google::protobuf::Reflection::func_name; \
+ }
+
+FUZZ_DEFINE_PRIMITIVE_GETTER(bool, GetBool);
+FUZZ_DEFINE_PRIMITIVE_GETTER(uint32_t, GetUInt32);
+FUZZ_DEFINE_PRIMITIVE_GETTER(int32_t, GetInt32);
+FUZZ_DEFINE_PRIMITIVE_GETTER(uint64_t, GetUInt64);
+FUZZ_DEFINE_PRIMITIVE_GETTER(int64_t, GetInt64);
+FUZZ_DEFINE_PRIMITIVE_GETTER(double, GetDouble);
+FUZZ_DEFINE_PRIMITIVE_GETTER(float, GetFloat);
+
+// ActionPerformer extracts arguments from the protobuf message, and then call FuzzFunction
+// with these arguments.
+template <typename FuzzFunction, typename Signature, typename Enabled = void>
+struct ActionPerformerImpl; // undefined
+
+template <typename FuzzFunction, typename MessageProto>
+struct ActionPerformerImpl<
+ FuzzFunction, void(const MessageProto&),
+ typename std::enable_if_t<std::is_base_of_v<google::protobuf::Message, MessageProto>>> {
+ static typename FuzzFunction::ReturnType Invoke(
+ typename FuzzFunction::ClassType* module, const google::protobuf::Message& action_proto,
+ const google::protobuf::FieldDescriptor* field_desc) {
+ const MessageProto& arg = CheckedCast<std::remove_reference_t<MessageProto>>(
+ action_proto.GetReflection()->GetMessage(action_proto, field_desc));
+ return FuzzFunction::ImplBody(module, arg);
+ }
+};
+
+template <typename FuzzFunction, typename Primitive>
+struct ActionPerformerImpl<FuzzFunction, void(Primitive),
+ typename std::enable_if_t<std::is_arithmetic_v<Primitive>>> {
+ static typename FuzzFunction::ReturnType Invoke(
+ typename FuzzFunction::ClassType* module, const google::protobuf::Message& action_proto,
+ const google::protobuf::FieldDescriptor* field_desc) {
+ Primitive arg = std::invoke(PrimitiveGetter<Primitive>::fp, action_proto.GetReflection(),
+ action_proto, field_desc);
+ return FuzzFunction::ImplBody(module, arg);
+ }
+};
+
+template <typename FuzzFunction>
+struct ActionPerformerImpl<FuzzFunction, void()> {
+ static typename FuzzFunction::ReturnType Invoke(typename FuzzFunction::ClassType* module,
+ const google::protobuf::Message&,
+ const google::protobuf::FieldDescriptor*) {
+ return FuzzFunction::ImplBody(module);
+ }
+};
+
+template <typename FuzzFunction>
+struct ActionPerformerImpl<FuzzFunction, void(const std::string&)> {
+ static typename FuzzFunction::ReturnType Invoke(
+ typename FuzzFunction::ClassType* module, const google::protobuf::Message& action_proto,
+ const google::protobuf::FieldDescriptor* field_desc) {
+ std::string scratch;
+ const std::string& arg = action_proto.GetReflection()->GetStringReference(
+ action_proto, field_desc, &scratch);
+ return FuzzFunction::ImplBody(module, arg);
+ }
+};
+
+template <typename FuzzFunction>
+struct ActionPerformer : ActionPerformerImpl<FuzzFunction, typename FuzzFunction::Signature> {};
+
+} // namespace android::fuzz
+
+// Fuzz existing C++ class, ClassType, with a collection of functions under the name Action.
+//
+// Prerequisite: ActionProto must be defined in Protobuf to describe possible actions:
+// message FooActionProto {
+// message NoArgs {}
+// oneof value {
+// bool do_foo = 1;
+// NoArgs do_bar = 1;
+// }
+// }
+// Use it to fuzz a C++ class Foo by doing the following:
+// FUZZ_CLASS(Foo, FooAction)
+// After linking functions of Foo to FooAction, execute all actions by:
+// FooAction::ExecuteAll(foo_object, action_protos)
+#define FUZZ_CLASS(Class, Action) \
+ class Action { \
+ public: \
+ using Proto = Action##Proto; \
+ using ClassType = Class; \
+ using FunctionMap = android::fuzz::FunctionMap<Class>; \
+ static FunctionMap* GetFunctionMap() { \
+ static Action::FunctionMap map; \
+ return ↦ \
+ } \
+ static void ExecuteAll(Class* module, \
+ const google::protobuf::RepeatedPtrField<Proto>& action_protos) { \
+ [[maybe_unused]] static int consistent = android::fuzz::CheckConsistency<Action>(); \
+ android::fuzz::ExecuteAllActionProtos<Action>(module, action_protos); \
+ } \
+ }
+
+#define FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName) Action##_##FunctionName
+#define FUZZ_FUNCTION_TAG_NAME(FunctionName) k##FunctionName
+
+// Implement an action defined in protobuf. Example:
+// message FooActionProto {
+// oneof value {
+// bool do_foo = 1;
+// }
+// }
+// class Foo { public: void DoAwesomeFoo(bool arg); };
+// FUZZ_OBJECT(FooAction, Foo);
+// FUZZ_FUNCTION(FooAction, DoFoo, void, IFoo* module, bool arg) {
+// module->DoAwesomeFoo(arg);
+// }
+// The name DoFoo is the camel case name of the action in protobuf definition of FooActionProto.
+#define FUZZ_FUNCTION(Action, FunctionName, Return, ModuleArg, ...) \
+ class FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName) { \
+ public: \
+ using ActionType = Action; \
+ using ClassType = Action::ClassType; \
+ using ReturnType = Return; \
+ using Signature = void(__VA_ARGS__); \
+ static constexpr const char name[] = #FunctionName; \
+ static constexpr const auto tag = \
+ Action::Proto::ValueCase::FUZZ_FUNCTION_TAG_NAME(FunctionName); \
+ static ReturnType ImplBody(ModuleArg, ##__VA_ARGS__); \
+ \
+ private: \
+ static bool registered_; \
+ }; \
+ auto FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName)::registered_ = ([] { \
+ auto tag = FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName)::tag; \
+ auto func = &::android::fuzz::ActionPerformer<FUZZ_FUNCTION_CLASS_NAME( \
+ Action, FunctionName)>::Invoke; \
+ Action::GetFunctionMap()->CheckEmplace(tag, func); \
+ return true; \
+ })(); \
+ Return FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName)::ImplBody(ModuleArg, ##__VA_ARGS__)
+
+// Implement a simple action by linking it to the function with the same name. Example:
+// message FooActionProto {
+// message NoArgs {}
+// oneof value {
+// NoArgs do_bar = 1;
+// }
+// }
+// class Foo { public void DoBar(); };
+// FUZZ_OBJECT(FooAction, Foo);
+// FUZZ_FUNCTION(FooAction, DoBar);
+// The name DoBar is the camel case name of the action in protobuf definition of FooActionProto, and
+// also the name of the function of Foo.
+#define FUZZ_SIMPLE_FUNCTION(Action, FunctionName) \
+ FUZZ_FUNCTION(Action, FunctionName, \
+ decltype(std::declval<Action::ClassType>().FunctionName()), \
+ Action::ClassType* module) { \
+ return module->FunctionName(); \
+ }
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_device_info.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_device_info.h
new file mode 100644
index 0000000..ef9d648
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_device_info.h
@@ -0,0 +1,37 @@
+// 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 <libsnapshot/snapshot.h>
+
+#include <gmock/gmock.h>
+
+namespace android::snapshot {
+
+class MockDeviceInfo : public SnapshotManager::IDeviceInfo {
+ public:
+ MOCK_METHOD(std::string, GetGsidDir, (), (const, override));
+ MOCK_METHOD(std::string, GetMetadataDir, (), (const, override));
+ MOCK_METHOD(std::string, GetSlotSuffix, (), (const, override));
+ MOCK_METHOD(std::string, GetOtherSlotSuffix, (), (const, override));
+ MOCK_METHOD(std::string, GetSuperDevice, (uint32_t slot), (const, override));
+ MOCK_METHOD(const android::fs_mgr::IPartitionOpener&, GetPartitionOpener, (), (const));
+ MOCK_METHOD(bool, IsOverlayfsSetup, (), (const, override));
+ MOCK_METHOD(bool, SetBootControlMergeStatus, (MergeStatus status), (override));
+ MOCK_METHOD(bool, SetSlotAsUnbootable, (unsigned int slot), (override));
+ MOCK_METHOD(bool, IsRecovery, (), (const, override));
+};
+
+} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
new file mode 100644
index 0000000..4457de3
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
@@ -0,0 +1,55 @@
+// 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 <libsnapshot/snapshot.h>
+
+#include <gmock/gmock.h>
+
+namespace android::snapshot {
+
+class MockSnapshotManager : public ISnapshotManager {
+ public:
+ MOCK_METHOD(bool, BeginUpdate, (), (override));
+ MOCK_METHOD(bool, CancelUpdate, (), (override));
+ MOCK_METHOD(bool, FinishedSnapshotWrites, (bool wipe), (override));
+ MOCK_METHOD(bool, InitiateMerge, (uint64_t * cow_file_size), (override));
+
+ MOCK_METHOD(UpdateState, ProcessUpdateState,
+ (const std::function<bool()>& callback, const std::function<bool()>& before_cancel),
+ (override));
+ MOCK_METHOD(UpdateState, GetUpdateState, (double* progress), (override));
+ MOCK_METHOD(Return, CreateUpdateSnapshots,
+ (const chromeos_update_engine::DeltaArchiveManifest& manifest), (override));
+ MOCK_METHOD(bool, MapUpdateSnapshot,
+ (const android::fs_mgr::CreateLogicalPartitionParams& params,
+ std::string* snapshot_path),
+ (override));
+ MOCK_METHOD(bool, UnmapUpdateSnapshot, (const std::string& target_partition_name), (override));
+ MOCK_METHOD(bool, NeedSnapshotsInFirstStageMount, (), (override));
+ MOCK_METHOD(bool, CreateLogicalAndSnapshotPartitions,
+ (const std::string& super_device, const std::chrono::milliseconds& timeout_ms),
+ (override));
+ MOCK_METHOD(bool, HandleImminentDataWipe, (const std::function<void()>& callback), (override));
+ MOCK_METHOD(bool, FinishMergeInRecovery, (), (override));
+ MOCK_METHOD(CreateResult, RecoveryCreateSnapshotDevices, (), (override));
+ MOCK_METHOD(CreateResult, RecoveryCreateSnapshotDevices,
+ (const std::unique_ptr<AutoDevice>& metadata_device), (override));
+ MOCK_METHOD(bool, Dump, (std::ostream & os), (override));
+ MOCK_METHOD(std::unique_ptr<AutoDevice>, EnsureMetadataMounted, (), (override));
+ MOCK_METHOD(ISnapshotMergeStats*, GetSnapshotMergeStatsInstance, (), (override));
+};
+
+} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/return.h b/fs_mgr/libsnapshot/include/libsnapshot/return.h
index 1f132fa..dedc445 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/return.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/return.h
@@ -30,7 +30,6 @@
enum class ErrorCode : int32_t {
SUCCESS = static_cast<int32_t>(FiemapStatus::ErrorCode::SUCCESS),
ERROR = static_cast<int32_t>(FiemapStatus::ErrorCode::ERROR),
- NEEDS_REBOOT = ERROR + 1,
NO_SPACE = static_cast<int32_t>(FiemapStatus::ErrorCode::NO_SPACE),
};
ErrorCode error_code() const { return error_code_; }
@@ -43,7 +42,6 @@
static Return Ok() { return Return(ErrorCode::SUCCESS); }
static Return Error() { return Return(ErrorCode::ERROR); }
static Return NoSpace(uint64_t size) { return Return(ErrorCode::NO_SPACE, size); }
- static Return NeedsReboot() { return Return(ErrorCode::NEEDS_REBOOT); }
// Does not set required_size_ properly even when status.error_code() == NO_SPACE.
explicit Return(const FiemapStatus& status)
: error_code_(FromFiemapStatusErrorCode(status.error_code())), required_size_(0) {}
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index b440c71..3c2c776 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -70,11 +70,14 @@
struct AutoDeleteSnapshot;
struct AutoDeviceList;
struct PartitionCowCreator;
+class ISnapshotMergeStats;
+class SnapshotMergeStats;
class SnapshotStatus;
static constexpr const std::string_view kCowGroupName = "cow";
-bool SourceCopyOperationIsClone(const chromeos_update_engine::InstallOperation& operation);
+bool OptimizeSourceCopyOperation(const chromeos_update_engine::InstallOperation& operation,
+ chromeos_update_engine::InstallOperation* optimized);
enum class CreateResult : unsigned int {
ERROR,
@@ -82,17 +85,7 @@
NOT_CREATED,
};
-class SnapshotManager final {
- using CreateLogicalPartitionParams = android::fs_mgr::CreateLogicalPartitionParams;
- using IPartitionOpener = android::fs_mgr::IPartitionOpener;
- using LpMetadata = android::fs_mgr::LpMetadata;
- using MetadataBuilder = android::fs_mgr::MetadataBuilder;
- using DeltaArchiveManifest = chromeos_update_engine::DeltaArchiveManifest;
- using MergeStatus = android::hardware::boot::V1_1::MergeStatus;
- using FiemapStatus = android::fiemap::FiemapStatus;
-
- friend class SnapshotMergeStats;
-
+class ISnapshotManager {
public:
// Dependency injection for testing.
class IDeviceInfo {
@@ -103,53 +96,45 @@
virtual std::string GetSlotSuffix() const = 0;
virtual std::string GetOtherSlotSuffix() const = 0;
virtual std::string GetSuperDevice(uint32_t slot) const = 0;
- virtual const IPartitionOpener& GetPartitionOpener() const = 0;
+ virtual const android::fs_mgr::IPartitionOpener& GetPartitionOpener() const = 0;
virtual bool IsOverlayfsSetup() const = 0;
- virtual bool SetBootControlMergeStatus(MergeStatus status) = 0;
+ virtual bool SetBootControlMergeStatus(
+ android::hardware::boot::V1_1::MergeStatus status) = 0;
virtual bool SetSlotAsUnbootable(unsigned int slot) = 0;
virtual bool IsRecovery() const = 0;
};
-
- ~SnapshotManager();
-
- // Return a new SnapshotManager instance, or null on error. The device
- // pointer is owned for the lifetime of SnapshotManager. If null, a default
- // instance will be created.
- static std::unique_ptr<SnapshotManager> New(IDeviceInfo* device = nullptr);
-
- // This is similar to New(), except designed specifically for first-stage
- // init or recovery.
- static std::unique_ptr<SnapshotManager> NewForFirstStageMount(IDeviceInfo* device = nullptr);
-
- // Helper function for first-stage init to check whether a SnapshotManager
- // might be needed to perform first-stage mounts.
- static bool IsSnapshotManagerNeeded();
+ virtual ~ISnapshotManager() = default;
// Begin an update. This must be called before creating any snapshots. It
// will fail if GetUpdateState() != None.
- bool BeginUpdate();
+ virtual bool BeginUpdate() = 0;
// Cancel an update; any snapshots will be deleted. This is allowed if the
// state == Initiated, None, or Unverified (before rebooting to the new
// slot).
- bool CancelUpdate();
+ virtual bool CancelUpdate() = 0;
// Mark snapshot writes as having completed. After this, new snapshots cannot
// be created, and the device must either cancel the OTA (either before
// rebooting or after rolling back), or merge the OTA.
// Before calling this function, all snapshots must be mapped.
- bool FinishedSnapshotWrites();
+ // If |wipe| is set to true, wipe is scheduled after reboot, and snapshots
+ // may need to be merged before wiping.
+ virtual bool FinishedSnapshotWrites(bool wipe) = 0;
- private:
// Initiate a merge on all snapshot devices. This should only be used after an
// update has been marked successful after booting.
- bool InitiateMerge();
+ virtual bool InitiateMerge(uint64_t* cow_file_size = nullptr) = 0;
// Perform any necessary post-boot actions. This should be run soon after
// /data is mounted.
//
// If a merge is in progress, this function will block until the merge is
- // completed. If a merge or update was cancelled, this will clean up any
+ // completed.
+ // - Callback is called periodically during the merge. If callback()
+ // returns false during the merge, ProcessUpdateState() will pause
+ // and returns Merging.
+ // If a merge or update was cancelled, this will clean up any
// update artifacts and return.
//
// Note that after calling this, GetUpdateState() may still return that a
@@ -169,26 +154,8 @@
//
// The optional callback allows the caller to periodically check the
// progress with GetUpdateState().
- UpdateState ProcessUpdateState(const std::function<void()>& callback = {});
-
- public:
- // Initiate the merge if necessary, then wait for the merge to finish.
- // See InitiateMerge() and ProcessUpdateState() for details.
- // Returns:
- // - None if no merge to initiate
- // - Unverified if called on the source slot
- // - MergeCompleted if merge is completed
- // - other states indicating an error has occurred
- UpdateState InitiateMergeAndWait(SnapshotMergeReport* report = nullptr);
-
- // Wait for the merge if rebooted into the new slot. Does NOT initiate a
- // merge. If the merge has not been initiated (but should be), wait.
- // Returns:
- // - Return::Ok(): there is no merge or merge finishes
- // - Return::NeedsReboot(): merge finishes but need a reboot before
- // applying the next update.
- // - Return::Error(): other irrecoverable errors
- Return WaitForMerge();
+ virtual UpdateState ProcessUpdateState(const std::function<bool()>& callback = {},
+ const std::function<bool()>& before_cancel = {}) = 0;
// Find the status of the current update, if any.
//
@@ -196,28 +163,31 @@
// Merging: Value in the range [0, 100]
// MergeCompleted: 100
// Other: 0
- UpdateState GetUpdateState(double* progress = nullptr);
+ virtual UpdateState GetUpdateState(double* progress = nullptr) = 0;
// Create necessary COW device / files for OTA clients. New logical partitions will be added to
// group "cow" in target_metadata. Regions of partitions of current_metadata will be
// "write-protected" and snapshotted.
- Return CreateUpdateSnapshots(const DeltaArchiveManifest& manifest);
+ virtual Return CreateUpdateSnapshots(
+ const chromeos_update_engine::DeltaArchiveManifest& manifest) = 0;
// Map a snapshotted partition for OTA clients to write to. Write-protected regions are
// determined previously in CreateSnapshots.
- bool MapUpdateSnapshot(const CreateLogicalPartitionParams& params, std::string* snapshot_path);
+ // |snapshot_path| must not be nullptr.
+ virtual bool MapUpdateSnapshot(const android::fs_mgr::CreateLogicalPartitionParams& params,
+ std::string* snapshot_path) = 0;
// Unmap a snapshot device that's previously mapped with MapUpdateSnapshot.
- bool UnmapUpdateSnapshot(const std::string& target_partition_name);
+ virtual bool UnmapUpdateSnapshot(const std::string& target_partition_name) = 0;
// If this returns true, first-stage mount must call
// CreateLogicalAndSnapshotPartitions rather than CreateLogicalPartitions.
- bool NeedSnapshotsInFirstStageMount();
+ virtual bool NeedSnapshotsInFirstStageMount() = 0;
// Perform first-stage mapping of snapshot targets. This replaces init's
// call to CreateLogicalPartitions when snapshots are present.
- bool CreateLogicalAndSnapshotPartitions(const std::string& super_device,
- const std::chrono::milliseconds& timeout_ms = {});
+ virtual bool CreateLogicalAndSnapshotPartitions(
+ const std::string& super_device, const std::chrono::milliseconds& timeout_ms = {}) = 0;
// This method should be called preceding any wipe or flash of metadata or
// userdata. It is only valid in recovery or fastbootd, and it ensures that
@@ -230,7 +200,11 @@
//
// Returns true on success (or nothing to do), false on failure. The
// optional callback fires periodically to query progress via GetUpdateState.
- bool HandleImminentDataWipe(const std::function<void()>& callback = {});
+ virtual bool HandleImminentDataWipe(const std::function<void()>& callback = {}) = 0;
+
+ // Force a merge to complete in recovery. This is similar to HandleImminentDataWipe
+ // but does not expect a data wipe after.
+ virtual bool FinishMergeInRecovery() = 0;
// This method is only allowed in recovery and is used as a helper to
// initialize the snapshot devices as a requirement to mount a snapshotted
@@ -241,10 +215,17 @@
// devices;
// - CreateResult::ERROR if a fatal error occurred, mounting /system should
// be aborted.
- CreateResult RecoveryCreateSnapshotDevices();
+ // This function mounts /metadata when called, and unmounts /metadata upon
+ // return.
+ virtual CreateResult RecoveryCreateSnapshotDevices() = 0;
+
+ // Same as RecoveryCreateSnapshotDevices(), but does not auto mount/umount
+ // /metadata.
+ virtual CreateResult RecoveryCreateSnapshotDevices(
+ const std::unique_ptr<AutoDevice>& metadata_device) = 0;
// Dump debug information.
- bool Dump(std::ostream& os);
+ virtual bool Dump(std::ostream& os) = 0;
// Ensure metadata directory is mounted in recovery. When the returned
// AutoDevice is destroyed, the metadata directory is automatically
@@ -260,7 +241,66 @@
// auto b = mgr->EnsureMetadataMounted(); // does nothing
// b.reset() // unmounts
// a.reset() // does nothing
- std::unique_ptr<AutoDevice> EnsureMetadataMounted();
+ virtual std::unique_ptr<AutoDevice> EnsureMetadataMounted() = 0;
+
+ // Return the associated ISnapshotMergeStats instance. Never null.
+ virtual ISnapshotMergeStats* GetSnapshotMergeStatsInstance() = 0;
+};
+
+class SnapshotManager final : public ISnapshotManager {
+ using CreateLogicalPartitionParams = android::fs_mgr::CreateLogicalPartitionParams;
+ using IPartitionOpener = android::fs_mgr::IPartitionOpener;
+ using LpMetadata = android::fs_mgr::LpMetadata;
+ using MetadataBuilder = android::fs_mgr::MetadataBuilder;
+ using DeltaArchiveManifest = chromeos_update_engine::DeltaArchiveManifest;
+ using MergeStatus = android::hardware::boot::V1_1::MergeStatus;
+ using FiemapStatus = android::fiemap::FiemapStatus;
+
+ friend class SnapshotMergeStats;
+
+ public:
+ ~SnapshotManager();
+
+ // Return a new SnapshotManager instance, or null on error. The device
+ // pointer is owned for the lifetime of SnapshotManager. If null, a default
+ // instance will be created.
+ static std::unique_ptr<SnapshotManager> New(IDeviceInfo* device = nullptr);
+
+ // This is similar to New(), except designed specifically for first-stage
+ // init or recovery.
+ static std::unique_ptr<SnapshotManager> NewForFirstStageMount(IDeviceInfo* device = nullptr);
+
+ // Helper function for first-stage init to check whether a SnapshotManager
+ // might be needed to perform first-stage mounts.
+ static bool IsSnapshotManagerNeeded();
+
+ // Helper function for second stage init to restorecon on the rollback indicator.
+ static std::string GetGlobalRollbackIndicatorPath();
+
+ // ISnapshotManager overrides.
+ bool BeginUpdate() override;
+ bool CancelUpdate() override;
+ bool FinishedSnapshotWrites(bool wipe) override;
+ bool InitiateMerge(uint64_t* cow_file_size = nullptr) override;
+ UpdateState ProcessUpdateState(const std::function<bool()>& callback = {},
+ const std::function<bool()>& before_cancel = {}) override;
+ UpdateState GetUpdateState(double* progress = nullptr) override;
+ Return CreateUpdateSnapshots(const DeltaArchiveManifest& manifest) override;
+ bool MapUpdateSnapshot(const CreateLogicalPartitionParams& params,
+ std::string* snapshot_path) override;
+ bool UnmapUpdateSnapshot(const std::string& target_partition_name) override;
+ bool NeedSnapshotsInFirstStageMount() override;
+ bool CreateLogicalAndSnapshotPartitions(
+ const std::string& super_device,
+ const std::chrono::milliseconds& timeout_ms = {}) override;
+ bool HandleImminentDataWipe(const std::function<void()>& callback = {}) override;
+ bool FinishMergeInRecovery() override;
+ CreateResult RecoveryCreateSnapshotDevices() override;
+ CreateResult RecoveryCreateSnapshotDevices(
+ const std::unique_ptr<AutoDevice>& metadata_device) override;
+ bool Dump(std::ostream& os) override;
+ std::unique_ptr<AutoDevice> EnsureMetadataMounted() override;
+ ISnapshotMergeStats* GetSnapshotMergeStatsInstance() override;
private:
FRIEND_TEST(SnapshotTest, CleanFirstStageMount);
@@ -284,6 +324,7 @@
friend class SnapshotUpdateTest;
friend class FlashAfterUpdateTest;
friend class LockTestConsumer;
+ friend class SnapshotFuzzEnv;
friend struct AutoDeleteCowImage;
friend struct AutoDeleteSnapshot;
friend struct PartitionCowCreator;
@@ -375,14 +416,23 @@
// Check for a cancelled or rolled back merge, returning true if such a
// condition was detected and handled.
- bool HandleCancelledUpdate(LockedFile* lock);
+ bool HandleCancelledUpdate(LockedFile* lock, const std::function<bool()>& before_cancel);
// Helper for HandleCancelledUpdate. Assumes booting from new slot.
bool AreAllSnapshotsCancelled(LockedFile* lock);
+ // Determine whether partition names in |snapshots| have been flashed and
+ // store result to |out|.
+ // Return true if values are successfully retrieved and false on error
+ // (e.g. super partition metadata cannot be read). When it returns true,
+ // |out| stores true for partitions that have been flashed and false for
+ // partitions that have not been flashed.
+ bool GetSnapshotFlashingStatus(LockedFile* lock, const std::vector<std::string>& snapshots,
+ std::map<std::string, bool>* out);
+
// Remove artifacts created by the update process, such as snapshots, and
// set the update state to None.
- bool RemoveAllUpdateState(LockedFile* lock);
+ bool RemoveAllUpdateState(LockedFile* lock, const std::function<bool()>& prolog = {});
// Interact with /metadata/ota.
std::unique_ptr<LockedFile> OpenLock(int lock_flags);
@@ -437,8 +487,8 @@
// UpdateState::MergeCompleted
// UpdateState::MergeFailed
// UpdateState::MergeNeedsReboot
- UpdateState CheckMergeState();
- UpdateState CheckMergeState(LockedFile* lock);
+ UpdateState CheckMergeState(const std::function<bool()>& before_cancel);
+ UpdateState CheckMergeState(LockedFile* lock, const std::function<bool()>& before_cancel);
UpdateState CheckTargetMergeState(LockedFile* lock, const std::string& name);
// Interact with status files under /metadata/ota/snapshots.
@@ -448,6 +498,7 @@
std::string GetSnapshotBootIndicatorPath();
std::string GetRollbackIndicatorPath();
+ std::string GetForwardMergeIndicatorPath();
// Return the name of the device holding the "snapshot" or "snapshot-merge"
// target. This may not be the final device presented via MapSnapshot(), if
@@ -513,11 +564,32 @@
std::string ReadUpdateSourceSlotSuffix();
+ // Helper for RemoveAllSnapshots.
+ // Check whether |name| should be deleted as a snapshot name.
+ bool ShouldDeleteSnapshot(LockedFile* lock, const std::map<std::string, bool>& flashing_status,
+ Slot current_slot, const std::string& name);
+
+ // Create or delete forward merge indicator given |wipe|. Iff wipe is scheduled,
+ // allow forward merge on FDR.
+ bool UpdateForwardMergeIndicator(bool wipe);
+
+ // Helper for HandleImminentDataWipe.
+ // Call ProcessUpdateState and handle states with special rules before data wipe. Specifically,
+ // if |allow_forward_merge| and allow-forward-merge indicator exists, initiate merge if
+ // necessary.
+ bool ProcessUpdateStateOnDataWipe(bool allow_forward_merge,
+ const std::function<bool()>& callback);
+
+ // Return device string of a mapped image, or if it is not available, the mapped image path.
+ bool GetMappedImageDeviceStringOrPath(const std::string& device_name,
+ std::string* device_string_or_mapped_path);
+
std::string gsid_dir_;
std::string metadata_dir_;
std::unique_ptr<IDeviceInfo> device_;
std::unique_ptr<IImageManager> images_;
bool has_local_image_manager_ = false;
+ bool in_factory_data_reset_ = false;
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h
new file mode 100644
index 0000000..d691d4f
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h
@@ -0,0 +1,73 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <chrono>
+#include <memory>
+
+#include <android/snapshot/snapshot.pb.h>
+#include <libsnapshot/snapshot.h>
+
+namespace android {
+namespace snapshot {
+
+class ISnapshotMergeStats {
+ public:
+ virtual ~ISnapshotMergeStats() = default;
+ // Called when merge starts or resumes.
+ virtual bool Start() = 0;
+ virtual void set_state(android::snapshot::UpdateState state) = 0;
+ virtual void set_cow_file_size(uint64_t cow_file_size) = 0;
+ virtual uint64_t cow_file_size() = 0;
+
+ // Called when merge ends. Properly clean up permanent storage.
+ class Result {
+ public:
+ virtual ~Result() {}
+ virtual const SnapshotMergeReport& report() const = 0;
+ // Time between successful Start() / Resume() to Finish().
+ virtual std::chrono::steady_clock::duration merge_time() const = 0;
+ };
+ // Return nullptr if any failure.
+ virtual std::unique_ptr<Result> Finish() = 0;
+};
+
+class SnapshotMergeStats : public ISnapshotMergeStats {
+ public:
+ // Not thread safe.
+ static SnapshotMergeStats* GetInstance(SnapshotManager& manager);
+
+ // ISnapshotMergeStats overrides
+ bool Start() override;
+ void set_state(android::snapshot::UpdateState state) override;
+ void set_cow_file_size(uint64_t cow_file_size) override;
+ uint64_t cow_file_size() override;
+ std::unique_ptr<Result> Finish() override;
+
+ private:
+ bool ReadState();
+ bool WriteState();
+ bool DeleteState();
+ SnapshotMergeStats(const std::string& path);
+
+ std::string path_;
+ SnapshotMergeReport report_;
+ // Time of the last successful Start() / Resume() call.
+ std::chrono::time_point<std::chrono::steady_clock> start_time_;
+ bool running_{false};
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
new file mode 100644
index 0000000..7a27fad
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
@@ -0,0 +1,53 @@
+// 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 <libsnapshot/snapshot.h>
+
+namespace android::snapshot {
+
+class SnapshotManagerStub : public ISnapshotManager {
+ public:
+ // Create a stubbed snapshot manager. All calls into the stub fails.
+ static std::unique_ptr<ISnapshotManager> New();
+
+ // ISnapshotManager overrides.
+ bool BeginUpdate() override;
+ bool CancelUpdate() override;
+ bool FinishedSnapshotWrites(bool wipe) override;
+ bool InitiateMerge(uint64_t* cow_file_size = nullptr) override;
+ UpdateState ProcessUpdateState(const std::function<bool()>& callback = {},
+ const std::function<bool()>& before_cancel = {}) override;
+ UpdateState GetUpdateState(double* progress = nullptr) override;
+ Return CreateUpdateSnapshots(
+ const chromeos_update_engine::DeltaArchiveManifest& manifest) override;
+ bool MapUpdateSnapshot(const android::fs_mgr::CreateLogicalPartitionParams& params,
+ std::string* snapshot_path) override;
+ bool UnmapUpdateSnapshot(const std::string& target_partition_name) override;
+ bool NeedSnapshotsInFirstStageMount() override;
+ bool CreateLogicalAndSnapshotPartitions(
+ const std::string& super_device,
+ const std::chrono::milliseconds& timeout_ms = {}) override;
+ bool HandleImminentDataWipe(const std::function<void()>& callback = {}) override;
+ bool FinishMergeInRecovery() override;
+ CreateResult RecoveryCreateSnapshotDevices() override;
+ CreateResult RecoveryCreateSnapshotDevices(
+ const std::unique_ptr<AutoDevice>& metadata_device) override;
+ bool Dump(std::ostream& os) override;
+ std::unique_ptr<AutoDevice> EnsureMetadataMounted() override;
+ ISnapshotMergeStats* GetSnapshotMergeStatsInstance() override;
+};
+
+} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/partition_cow_creator.cpp b/fs_mgr/libsnapshot/partition_cow_creator.cpp
index 61f5c0c..0df5664 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator.cpp
@@ -62,17 +62,68 @@
return false;
}
-bool SourceCopyOperationIsClone(const InstallOperation& operation) {
- using ChromeOSExtent = chromeos_update_engine::Extent;
- if (operation.src_extents().size() != operation.dst_extents().size()) {
+bool OptimizeSourceCopyOperation(const InstallOperation& operation, InstallOperation* optimized) {
+ if (operation.type() != InstallOperation::SOURCE_COPY) {
return false;
}
- return std::equal(operation.src_extents().begin(), operation.src_extents().end(),
- operation.dst_extents().begin(),
- [](const ChromeOSExtent& src, const ChromeOSExtent& dst) {
- return src.start_block() == dst.start_block() &&
- src.num_blocks() == dst.num_blocks();
- });
+
+ optimized->Clear();
+ optimized->set_type(InstallOperation::SOURCE_COPY);
+
+ const auto& src_extents = operation.src_extents();
+ const auto& dst_extents = operation.dst_extents();
+
+ // If input is empty, skip by returning an empty result.
+ if (src_extents.empty() && dst_extents.empty()) {
+ return true;
+ }
+
+ auto s_it = src_extents.begin();
+ auto d_it = dst_extents.begin();
+ uint64_t s_offset = 0; // offset within *s_it
+ uint64_t d_offset = 0; // offset within *d_it
+ bool is_optimized = false;
+
+ while (s_it != src_extents.end() || d_it != dst_extents.end()) {
+ if (s_it == src_extents.end() || d_it == dst_extents.end()) {
+ LOG(ERROR) << "number of blocks do not equal in src_extents and dst_extents";
+ return false;
+ }
+ if (s_it->num_blocks() <= s_offset || d_it->num_blocks() <= d_offset) {
+ LOG(ERROR) << "Offset goes out of bounds.";
+ return false;
+ }
+
+ // Check the next |step| blocks, where |step| is the min of remaining blocks in the current
+ // source extent and current destination extent.
+ auto s_step = s_it->num_blocks() - s_offset;
+ auto d_step = d_it->num_blocks() - d_offset;
+ auto step = std::min(s_step, d_step);
+
+ bool moved = s_it->start_block() + s_offset != d_it->start_block() + d_offset;
+ if (moved) {
+ // If the next |step| blocks are not copied to the same location, add them to result.
+ AppendExtent(optimized->mutable_src_extents(), s_it->start_block() + s_offset, step);
+ AppendExtent(optimized->mutable_dst_extents(), d_it->start_block() + d_offset, step);
+ } else {
+ // The next |step| blocks are optimized out.
+ is_optimized = true;
+ }
+
+ // Advance offsets by |step|, and go to the next non-empty extent if the current extent is
+ // depleted.
+ s_offset += step;
+ d_offset += step;
+ while (s_it != src_extents.end() && s_offset >= s_it->num_blocks()) {
+ ++s_it;
+ s_offset = 0;
+ }
+ while (d_it != dst_extents.end() && d_offset >= d_it->num_blocks()) {
+ ++d_it;
+ d_offset = 0;
+ }
+ }
+ return is_optimized;
}
void WriteExtent(DmSnapCowSizeCalculator* sc, const chromeos_update_engine::Extent& de,
@@ -101,12 +152,15 @@
if (operations == nullptr) return sc.cow_size_bytes();
for (const auto& iop : *operations) {
- // Do not allocate space for operations that are going to be skipped
+ const InstallOperation* written_op = &iop;
+ InstallOperation buf;
+ // Do not allocate space for extents that are going to be skipped
// during OTA application.
- if (iop.type() == InstallOperation::SOURCE_COPY && SourceCopyOperationIsClone(iop))
- continue;
+ if (iop.type() == InstallOperation::SOURCE_COPY && OptimizeSourceCopyOperation(iop, &buf)) {
+ written_op = &buf;
+ }
- for (const auto& de : iop.dst_extents()) {
+ for (const auto& de : written_op->dst_extents()) {
WriteExtent(&sc, de, sectors_per_block);
}
}
@@ -127,6 +181,13 @@
ret.snapshot_status.set_device_size(target_partition->size());
ret.snapshot_status.set_snapshot_size(target_partition->size());
+ if (ret.snapshot_status.snapshot_size() == 0) {
+ LOG(INFO) << "Not creating snapshot for partition " << ret.snapshot_status.name();
+ ret.snapshot_status.set_cow_partition_size(0);
+ ret.snapshot_status.set_cow_file_size(0);
+ return ret;
+ }
+
// Being the COW partition virtual, its size doesn't affect the storage
// memory that will be occupied by the target.
// The actual storage space is affected by the COW file, whose size depends
diff --git a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
index 9da3f05..adfb975 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
@@ -12,6 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <optional>
+#include <tuple>
+
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <libdm/dm.h>
@@ -26,6 +29,13 @@
using namespace android::fs_mgr;
+using chromeos_update_engine::InstallOperation;
+using UeExtent = chromeos_update_engine::Extent;
+using google::protobuf::RepeatedPtrField;
+using ::testing::Matches;
+using ::testing::Pointwise;
+using ::testing::Truly;
+
namespace android {
namespace snapshot {
@@ -36,20 +46,20 @@
};
TEST_F(PartitionCowCreatorTest, IntersectSelf) {
- constexpr uint64_t initial_size = 1_MiB;
- constexpr uint64_t final_size = 40_KiB;
+ constexpr uint64_t super_size = 1_MiB;
+ constexpr uint64_t partition_size = 40_KiB;
- auto builder_a = MetadataBuilder::New(initial_size, 1_KiB, 2);
+ auto builder_a = MetadataBuilder::New(super_size, 1_KiB, 2);
ASSERT_NE(builder_a, nullptr);
auto system_a = builder_a->AddPartition("system_a", LP_PARTITION_ATTR_READONLY);
ASSERT_NE(system_a, nullptr);
- ASSERT_TRUE(builder_a->ResizePartition(system_a, final_size));
+ ASSERT_TRUE(builder_a->ResizePartition(system_a, partition_size));
- auto builder_b = MetadataBuilder::New(initial_size, 1_KiB, 2);
+ auto builder_b = MetadataBuilder::New(super_size, 1_KiB, 2);
ASSERT_NE(builder_b, nullptr);
auto system_b = builder_b->AddPartition("system_b", LP_PARTITION_ATTR_READONLY);
ASSERT_NE(system_b, nullptr);
- ASSERT_TRUE(builder_b->ResizePartition(system_b, final_size));
+ ASSERT_TRUE(builder_b->ResizePartition(system_b, partition_size));
PartitionCowCreator creator{.target_metadata = builder_b.get(),
.target_suffix = "_b",
@@ -58,8 +68,8 @@
.current_suffix = "_a"};
auto ret = creator.Run();
ASSERT_TRUE(ret.has_value());
- ASSERT_EQ(final_size, ret->snapshot_status.device_size());
- ASSERT_EQ(final_size, ret->snapshot_status.snapshot_size());
+ ASSERT_EQ(partition_size, ret->snapshot_status.device_size());
+ ASSERT_EQ(partition_size, ret->snapshot_status.snapshot_size());
}
TEST_F(PartitionCowCreatorTest, Holes) {
@@ -108,20 +118,20 @@
using RepeatedInstallOperationPtr = google::protobuf::RepeatedPtrField<InstallOperation>;
using Extent = chromeos_update_engine::Extent;
- constexpr uint64_t initial_size = 50_MiB;
- constexpr uint64_t final_size = 40_MiB;
+ constexpr uint64_t super_size = 50_MiB;
+ constexpr uint64_t partition_size = 40_MiB;
- auto builder_a = MetadataBuilder::New(initial_size, 1_KiB, 2);
+ auto builder_a = MetadataBuilder::New(super_size, 1_KiB, 2);
ASSERT_NE(builder_a, nullptr);
auto system_a = builder_a->AddPartition("system_a", LP_PARTITION_ATTR_READONLY);
ASSERT_NE(system_a, nullptr);
- ASSERT_TRUE(builder_a->ResizePartition(system_a, final_size));
+ ASSERT_TRUE(builder_a->ResizePartition(system_a, partition_size));
- auto builder_b = MetadataBuilder::New(initial_size, 1_KiB, 2);
+ auto builder_b = MetadataBuilder::New(super_size, 1_KiB, 2);
ASSERT_NE(builder_b, nullptr);
auto system_b = builder_b->AddPartition("system_b", LP_PARTITION_ATTR_READONLY);
ASSERT_NE(system_b, nullptr);
- ASSERT_TRUE(builder_b->ResizePartition(system_b, final_size));
+ ASSERT_TRUE(builder_b->ResizePartition(system_b, partition_size));
const uint64_t block_size = builder_b->logical_block_size();
const uint64_t chunk_size = kSnapshotChunkSize * dm::kSectorSize;
@@ -187,6 +197,31 @@
ASSERT_EQ(6 * chunk_size, cow_device_size(iopv, builder_a.get(), builder_b.get(), system_b));
}
+TEST_F(PartitionCowCreatorTest, Zero) {
+ constexpr uint64_t super_size = 1_MiB;
+ auto builder_a = MetadataBuilder::New(super_size, 1_KiB, 2);
+ ASSERT_NE(builder_a, nullptr);
+
+ auto builder_b = MetadataBuilder::New(super_size, 1_KiB, 2);
+ ASSERT_NE(builder_b, nullptr);
+ auto system_b = builder_b->AddPartition("system_b", LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(system_b, nullptr);
+
+ PartitionCowCreator creator{.target_metadata = builder_b.get(),
+ .target_suffix = "_b",
+ .target_partition = system_b,
+ .current_metadata = builder_a.get(),
+ .current_suffix = "_a",
+ .operations = nullptr};
+
+ auto ret = creator.Run();
+
+ ASSERT_EQ(0u, ret->snapshot_status.device_size());
+ ASSERT_EQ(0u, ret->snapshot_status.snapshot_size());
+ ASSERT_EQ(0u, ret->snapshot_status.cow_file_size());
+ ASSERT_EQ(0u, ret->snapshot_status.cow_partition_size());
+}
+
TEST(DmSnapshotInternals, CowSizeCalculator) {
DmSnapCowSizeCalculator cc(512, 8);
unsigned long int b;
@@ -213,5 +248,76 @@
}
}
+void BlocksToExtents(const std::vector<uint64_t>& blocks,
+ google::protobuf::RepeatedPtrField<UeExtent>* extents) {
+ for (uint64_t block : blocks) {
+ AppendExtent(extents, block, 1);
+ }
+}
+
+template <typename T>
+std::vector<uint64_t> ExtentsToBlocks(const T& extents) {
+ std::vector<uint64_t> blocks;
+ for (const auto& extent : extents) {
+ for (uint64_t offset = 0; offset < extent.num_blocks(); ++offset) {
+ blocks.push_back(extent.start_block() + offset);
+ }
+ }
+ return blocks;
+}
+
+InstallOperation CreateCopyOp(const std::vector<uint64_t>& src_blocks,
+ const std::vector<uint64_t>& dst_blocks) {
+ InstallOperation op;
+ op.set_type(InstallOperation::SOURCE_COPY);
+ BlocksToExtents(src_blocks, op.mutable_src_extents());
+ BlocksToExtents(dst_blocks, op.mutable_dst_extents());
+ return op;
+}
+
+// ExtentEqual(tuple<UeExtent, UeExtent>)
+MATCHER(ExtentEqual, "") {
+ auto&& [a, b] = arg;
+ return a.start_block() == b.start_block() && a.num_blocks() == b.num_blocks();
+}
+
+struct OptimizeOperationTestParam {
+ InstallOperation input;
+ std::optional<InstallOperation> expected_output;
+};
+
+class OptimizeOperationTest : public ::testing::TestWithParam<OptimizeOperationTestParam> {};
+TEST_P(OptimizeOperationTest, Test) {
+ InstallOperation actual_output;
+ EXPECT_EQ(GetParam().expected_output.has_value(),
+ OptimizeSourceCopyOperation(GetParam().input, &actual_output))
+ << "OptimizeSourceCopyOperation should "
+ << (GetParam().expected_output.has_value() ? "succeed" : "fail");
+ if (!GetParam().expected_output.has_value()) return;
+ EXPECT_THAT(actual_output.src_extents(),
+ Pointwise(ExtentEqual(), GetParam().expected_output->src_extents()));
+ EXPECT_THAT(actual_output.dst_extents(),
+ Pointwise(ExtentEqual(), GetParam().expected_output->dst_extents()));
+}
+
+std::vector<OptimizeOperationTestParam> GetOptimizeOperationTestParams() {
+ return {
+ {CreateCopyOp({}, {}), CreateCopyOp({}, {})},
+ {CreateCopyOp({1, 2, 4}, {1, 2, 4}), CreateCopyOp({}, {})},
+ {CreateCopyOp({1, 2, 3}, {4, 5, 6}), std::nullopt},
+ {CreateCopyOp({3, 2}, {1, 2}), CreateCopyOp({3}, {1})},
+ {CreateCopyOp({5, 6, 3, 4, 1, 2}, {1, 2, 3, 4, 5, 6}),
+ CreateCopyOp({5, 6, 1, 2}, {1, 2, 5, 6})},
+ {CreateCopyOp({1, 2, 3, 5, 5, 6}, {5, 6, 3, 4, 1, 2}),
+ CreateCopyOp({1, 2, 5, 5, 6}, {5, 6, 4, 1, 2})},
+ {CreateCopyOp({1, 2, 5, 6, 9, 10}, {1, 4, 5, 6, 7, 8}),
+ CreateCopyOp({2, 9, 10}, {4, 7, 8})},
+ {CreateCopyOp({2, 3, 3, 4, 4}, {1, 2, 3, 4, 5}), CreateCopyOp({2, 3, 4}, {1, 2, 5})},
+ };
+}
+
+INSTANTIATE_TEST_CASE_P(Snapshot, OptimizeOperationTest,
+ ::testing::ValuesIn(GetOptimizeOperationTestParams()));
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/power_test.cpp b/fs_mgr/libsnapshot/power_test.cpp
new file mode 100644
index 0000000..4d2548a
--- /dev/null
+++ b/fs_mgr/libsnapshot/power_test.cpp
@@ -0,0 +1,559 @@
+//
+// 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 <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <chrono>
+#include <iostream>
+#include <random>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/parsedouble.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <ext4_utils/ext4_utils.h>
+#include <fstab/fstab.h>
+#include <libdm/dm.h>
+#include <libfiemap/image_manager.h>
+
+using namespace std::chrono_literals;
+using namespace std::string_literals;
+using android::base::borrowed_fd;
+using android::base::unique_fd;
+using android::dm::DeviceMapper;
+using android::dm::DmDeviceState;
+using android::dm::DmTable;
+using android::dm::DmTargetSnapshot;
+using android::dm::SnapshotStorageMode;
+using android::fiemap::ImageManager;
+using android::fs_mgr::Fstab;
+
+namespace android {
+namespace snapshot {
+
+static void usage() {
+ std::cerr << "Usage:\n";
+ std::cerr << " create <orig-payload> <new-payload>\n";
+ std::cerr << "\n";
+ std::cerr << " Create a snapshot device containing the contents of\n";
+ std::cerr << " orig-payload, and then write the contents of new-payload.\n";
+ std::cerr << " The original files are not modified.\n";
+ std::cerr << "\n";
+ std::cerr << " merge <fail-rate>\n";
+ std::cerr << "\n";
+ std::cerr << " Merge the snapshot previously started by create, and wait\n";
+ std::cerr << " for it to complete. Once done, it is compared to the\n";
+ std::cerr << " new-payload for consistency. The original files are not \n";
+ std::cerr << " modified. If a fail-rate is passed (as a fraction between 0\n";
+ std::cerr << " and 100), every 10ms the device has that percent change of\n";
+ std::cerr << " injecting a kernel crash.\n";
+ std::cerr << "\n";
+ std::cerr << " check <new-payload>\n";
+ std::cerr << " Verify that all artifacts are correct after a merge\n";
+ std::cerr << " completes.\n";
+ std::cerr << "\n";
+ std::cerr << " cleanup\n";
+ std::cerr << " Remove all ImageManager artifacts from create/merge.\n";
+}
+
+class PowerTest final {
+ public:
+ PowerTest();
+ bool Run(int argc, char** argv);
+
+ private:
+ bool OpenImageManager();
+ bool Create(int argc, char** argv);
+ bool Merge(int argc, char** argv);
+ bool Check(int argc, char** argv);
+ bool Cleanup();
+ bool CleanupImage(const std::string& name);
+ bool SetupImages(const std::string& first_file, borrowed_fd second_fd);
+ bool MapImages();
+ bool MapSnapshot(SnapshotStorageMode mode);
+ bool GetMergeStatus(DmTargetSnapshot::Status* status);
+
+ static constexpr char kSnapshotName[] = "snapshot-power-test";
+ static constexpr char kSnapshotImageName[] = "snapshot-power-test-image";
+ static constexpr char kSnapshotCowName[] = "snapshot-power-test-cow";
+
+ DeviceMapper& dm_;
+ std::unique_ptr<ImageManager> images_;
+ std::string image_path_;
+ std::string cow_path_;
+ std::string snapshot_path_;
+};
+
+PowerTest::PowerTest() : dm_(DeviceMapper::Instance()) {}
+
+bool PowerTest::Run([[maybe_unused]] int argc, [[maybe_unused]] char** argv) {
+ if (!OpenImageManager()) {
+ return false;
+ }
+
+ if (argc < 2) {
+ usage();
+ return false;
+ }
+ if (argv[1] == "create"s) {
+ return Create(argc, argv);
+ } else if (argv[1] == "merge"s) {
+ return Merge(argc, argv);
+ } else if (argv[1] == "check"s) {
+ return Check(argc, argv);
+ } else if (argv[1] == "cleanup"s) {
+ return Cleanup();
+ } else {
+ usage();
+ return false;
+ }
+}
+
+bool PowerTest::OpenImageManager() {
+ std::vector<std::string> dirs = {
+ "/data/gsi/test",
+ "/metadata/gsi/test",
+ };
+ for (const auto& dir : dirs) {
+ if (mkdir(dir.c_str(), 0700) && errno != EEXIST) {
+ std::cerr << "mkdir " << dir << ": " << strerror(errno) << "\n";
+ return false;
+ }
+ }
+
+ images_ = ImageManager::Open("/metadata/gsi/test", "/data/gsi/test");
+ if (!images_) {
+ std::cerr << "Could not open ImageManager\n";
+ return false;
+ }
+ return true;
+}
+
+bool PowerTest::Create(int argc, char** argv) {
+ if (argc < 4) {
+ usage();
+ return false;
+ }
+
+ std::string first = argv[2];
+ std::string second = argv[3];
+
+ unique_fd second_fd(open(second.c_str(), O_RDONLY));
+ if (second_fd < 0) {
+ std::cerr << "open " << second << ": " << strerror(errno) << "\n";
+ return false;
+ }
+
+ if (!Cleanup()) {
+ return false;
+ }
+ if (!SetupImages(first, second_fd)) {
+ return false;
+ }
+ if (!MapSnapshot(SnapshotStorageMode::Persistent)) {
+ return false;
+ }
+
+ struct stat s;
+ if (fstat(second_fd, &s)) {
+ std::cerr << "fstat " << second << ": " << strerror(errno) << "\n";
+ return false;
+ }
+
+ unique_fd snap_fd(open(snapshot_path_.c_str(), O_WRONLY));
+ if (snap_fd < 0) {
+ std::cerr << "open " << snapshot_path_ << ": " << strerror(errno) << "\n";
+ return false;
+ }
+
+ uint8_t chunk[4096];
+ uint64_t written = 0;
+ while (written < s.st_size) {
+ uint64_t remaining = s.st_size - written;
+ size_t bytes = (size_t)std::min((uint64_t)sizeof(chunk), remaining);
+ if (!android::base::ReadFully(second_fd, chunk, bytes)) {
+ std::cerr << "read " << second << ": " << strerror(errno) << "\n";
+ return false;
+ }
+ if (!android::base::WriteFully(snap_fd, chunk, bytes)) {
+ std::cerr << "write " << snapshot_path_ << ": " << strerror(errno) << "\n";
+ return false;
+ }
+ written += bytes;
+ }
+ if (fsync(snap_fd)) {
+ std::cerr << "fsync: " << strerror(errno) << "\n";
+ return false;
+ }
+
+ sync();
+
+ snap_fd = {};
+ if (!dm_.DeleteDeviceIfExists(kSnapshotName)) {
+ std::cerr << "could not delete dm device " << kSnapshotName << "\n";
+ return false;
+ }
+ if (!images_->UnmapImageIfExists(kSnapshotImageName)) {
+ std::cerr << "failed to unmap " << kSnapshotImageName << "\n";
+ return false;
+ }
+ if (!images_->UnmapImageIfExists(kSnapshotCowName)) {
+ std::cerr << "failed to unmap " << kSnapshotImageName << "\n";
+ return false;
+ }
+ return true;
+}
+
+bool PowerTest::Cleanup() {
+ if (!dm_.DeleteDeviceIfExists(kSnapshotName)) {
+ std::cerr << "could not delete dm device " << kSnapshotName << "\n";
+ return false;
+ }
+ if (!CleanupImage(kSnapshotImageName) || !CleanupImage(kSnapshotCowName)) {
+ return false;
+ }
+ return true;
+}
+
+bool PowerTest::CleanupImage(const std::string& name) {
+ if (!images_->UnmapImageIfExists(name)) {
+ std::cerr << "failed to unmap " << name << "\n";
+ return false;
+ }
+ if (images_->BackingImageExists(name) && !images_->DeleteBackingImage(name)) {
+ std::cerr << "failed to delete " << name << "\n";
+ return false;
+ }
+ return true;
+}
+
+bool PowerTest::SetupImages(const std::string& first, borrowed_fd second_fd) {
+ unique_fd first_fd(open(first.c_str(), O_RDONLY));
+ if (first_fd < 0) {
+ std::cerr << "open " << first << ": " << strerror(errno) << "\n";
+ return false;
+ }
+
+ struct stat s1, s2;
+ if (fstat(first_fd.get(), &s1)) {
+ std::cerr << "first stat: " << strerror(errno) << "\n";
+ return false;
+ }
+ if (fstat(second_fd.get(), &s2)) {
+ std::cerr << "second stat: " << strerror(errno) << "\n";
+ return false;
+ }
+
+ // Pick the bigger size of both images, rounding up to the nearest block.
+ uint64_t s1_size = (s1.st_size + 4095) & ~uint64_t(4095);
+ uint64_t s2_size = (s2.st_size + 4095) & ~uint64_t(4095);
+ uint64_t image_size = std::max(s1_size, s2_size) + (1024 * 1024 * 128);
+ if (!images_->CreateBackingImage(kSnapshotImageName, image_size, 0, nullptr)) {
+ std::cerr << "failed to create " << kSnapshotImageName << "\n";
+ return false;
+ }
+ // Use the same size for the cow.
+ if (!images_->CreateBackingImage(kSnapshotCowName, image_size, 0, nullptr)) {
+ std::cerr << "failed to create " << kSnapshotCowName << "\n";
+ return false;
+ }
+ if (!MapImages()) {
+ return false;
+ }
+
+ unique_fd image_fd(open(image_path_.c_str(), O_WRONLY));
+ if (image_fd < 0) {
+ std::cerr << "open: " << image_path_ << ": " << strerror(errno) << "\n";
+ return false;
+ }
+
+ uint8_t chunk[4096];
+ uint64_t written = 0;
+ while (written < s1.st_size) {
+ uint64_t remaining = s1.st_size - written;
+ size_t bytes = (size_t)std::min((uint64_t)sizeof(chunk), remaining);
+ if (!android::base::ReadFully(first_fd, chunk, bytes)) {
+ std::cerr << "read: " << strerror(errno) << "\n";
+ return false;
+ }
+ if (!android::base::WriteFully(image_fd, chunk, bytes)) {
+ std::cerr << "write: " << strerror(errno) << "\n";
+ return false;
+ }
+ written += bytes;
+ }
+ if (fsync(image_fd)) {
+ std::cerr << "fsync: " << strerror(errno) << "\n";
+ return false;
+ }
+
+ // Zero the first block of the COW.
+ unique_fd cow_fd(open(cow_path_.c_str(), O_WRONLY));
+ if (cow_fd < 0) {
+ std::cerr << "open: " << cow_path_ << ": " << strerror(errno) << "\n";
+ return false;
+ }
+
+ memset(chunk, 0, sizeof(chunk));
+ if (!android::base::WriteFully(cow_fd, chunk, sizeof(chunk))) {
+ std::cerr << "read: " << strerror(errno) << "\n";
+ return false;
+ }
+ if (fsync(cow_fd)) {
+ std::cerr << "fsync: " << strerror(errno) << "\n";
+ return false;
+ }
+ return true;
+}
+
+bool PowerTest::MapImages() {
+ if (!images_->MapImageDevice(kSnapshotImageName, 10s, &image_path_)) {
+ std::cerr << "failed to map " << kSnapshotImageName << "\n";
+ return false;
+ }
+ if (!images_->MapImageDevice(kSnapshotCowName, 10s, &cow_path_)) {
+ std::cerr << "failed to map " << kSnapshotCowName << "\n";
+ return false;
+ }
+ return true;
+}
+
+bool PowerTest::MapSnapshot(SnapshotStorageMode mode) {
+ uint64_t sectors;
+ {
+ unique_fd fd(open(image_path_.c_str(), O_RDONLY));
+ if (fd < 0) {
+ std::cerr << "open: " << image_path_ << ": " << strerror(errno) << "\n";
+ return false;
+ }
+ sectors = get_block_device_size(fd) / 512;
+ }
+
+ DmTable table;
+ table.Emplace<DmTargetSnapshot>(0, sectors, image_path_, cow_path_, mode, 8);
+ if (!dm_.CreateDevice(kSnapshotName, table, &snapshot_path_, 10s)) {
+ std::cerr << "failed to create snapshot device\n";
+ return false;
+ }
+ return true;
+}
+
+bool PowerTest::GetMergeStatus(DmTargetSnapshot::Status* status) {
+ std::vector<DeviceMapper::TargetInfo> targets;
+ if (!dm_.GetTableStatus(kSnapshotName, &targets)) {
+ std::cerr << "failed to get merge status\n";
+ return false;
+ }
+ if (targets.size() != 1) {
+ std::cerr << "merge device has wrong number of targets\n";
+ return false;
+ }
+ if (!DmTargetSnapshot::ParseStatusText(targets[0].data, status)) {
+ std::cerr << "could not parse merge target status text\n";
+ return false;
+ }
+ return true;
+}
+
+static std::string GetUserdataBlockDeviceName() {
+ Fstab fstab;
+ if (!ReadFstabFromFile("/proc/mounts", &fstab)) {
+ return {};
+ }
+
+ auto entry = android::fs_mgr::GetEntryForMountPoint(&fstab, "/data");
+ if (!entry) {
+ return {};
+ }
+
+ auto prefix = "/dev/block/"s;
+ if (!android::base::StartsWith(entry->blk_device, prefix)) {
+ return {};
+ }
+ return entry->blk_device.substr(prefix.size());
+}
+
+bool PowerTest::Merge(int argc, char** argv) {
+ // Start an f2fs GC to really stress things. :TODO: figure out data device
+ auto userdata_dev = GetUserdataBlockDeviceName();
+ if (userdata_dev.empty()) {
+ std::cerr << "could not locate userdata block device\n";
+ return false;
+ }
+
+ auto cmd =
+ android::base::StringPrintf("echo 1 > /sys/fs/f2fs/%s/gc_urgent", userdata_dev.c_str());
+ system(cmd.c_str());
+
+ if (dm_.GetState(kSnapshotName) == DmDeviceState::INVALID) {
+ if (!MapImages()) {
+ return false;
+ }
+ if (!MapSnapshot(SnapshotStorageMode::Merge)) {
+ return false;
+ }
+ }
+
+ std::random_device r;
+ std::default_random_engine re(r());
+ std::uniform_real_distribution<double> dist(0.0, 100.0);
+
+ std::optional<double> failure_rate;
+ if (argc >= 3) {
+ double d;
+ if (!android::base::ParseDouble(argv[2], &d)) {
+ std::cerr << "Could not parse failure rate as double: " << argv[2] << "\n";
+ return false;
+ }
+ failure_rate = d;
+ }
+
+ while (true) {
+ DmTargetSnapshot::Status status;
+ if (!GetMergeStatus(&status)) {
+ return false;
+ }
+ if (!status.error.empty()) {
+ std::cerr << "merge reported error: " << status.error << "\n";
+ return false;
+ }
+ if (status.sectors_allocated == status.metadata_sectors) {
+ break;
+ }
+
+ std::cerr << status.sectors_allocated << " / " << status.metadata_sectors << "\n";
+
+ if (failure_rate && *failure_rate >= dist(re)) {
+ system("echo 1 > /proc/sys/kernel/sysrq");
+ system("echo c > /proc/sysrq-trigger");
+ }
+
+ std::this_thread::sleep_for(10ms);
+ }
+
+ std::cout << "Merge completed.\n";
+ return true;
+}
+
+bool PowerTest::Check([[maybe_unused]] int argc, [[maybe_unused]] char** argv) {
+ if (argc < 3) {
+ std::cerr << "Expected argument: <new-image-path>\n";
+ return false;
+ }
+ std::string md_path, image_path;
+ std::string canonical_path = argv[2];
+
+ if (!dm_.GetDmDevicePathByName(kSnapshotName, &md_path)) {
+ std::cerr << "could not get dm-path for merge device\n";
+ return false;
+ }
+ if (!images_->GetMappedImageDevice(kSnapshotImageName, &image_path)) {
+ std::cerr << "could not get image path\n";
+ return false;
+ }
+
+ unique_fd md_fd(open(md_path.c_str(), O_RDONLY));
+ if (md_fd < 0) {
+ std::cerr << "open: " << md_path << ": " << strerror(errno) << "\n";
+ return false;
+ }
+ unique_fd image_fd(open(image_path.c_str(), O_RDONLY));
+ if (image_fd < 0) {
+ std::cerr << "open: " << image_path << ": " << strerror(errno) << "\n";
+ return false;
+ }
+ unique_fd canonical_fd(open(canonical_path.c_str(), O_RDONLY));
+ if (canonical_fd < 0) {
+ std::cerr << "open: " << canonical_path << ": " << strerror(errno) << "\n";
+ return false;
+ }
+
+ struct stat s;
+ if (fstat(canonical_fd, &s)) {
+ std::cerr << "fstat: " << canonical_path << ": " << strerror(errno) << "\n";
+ return false;
+ }
+ uint64_t canonical_size = s.st_size;
+ uint64_t md_size = get_block_device_size(md_fd);
+ uint64_t image_size = get_block_device_size(image_fd);
+ if (image_size != md_size) {
+ std::cerr << "image size does not match merge device size\n";
+ return false;
+ }
+ if (canonical_size > image_size) {
+ std::cerr << "canonical size " << canonical_size << " is greater than image size "
+ << image_size << "\n";
+ return false;
+ }
+
+ constexpr size_t kBlockSize = 4096;
+ uint8_t canonical_buffer[kBlockSize];
+ uint8_t image_buffer[kBlockSize];
+ uint8_t md_buffer[kBlockSize];
+
+ uint64_t remaining = canonical_size;
+ uint64_t blockno = 0;
+ while (remaining) {
+ size_t bytes = (size_t)std::min((uint64_t)kBlockSize, remaining);
+ if (!android::base::ReadFully(canonical_fd, canonical_buffer, bytes)) {
+ std::cerr << "read: " << canonical_buffer << ": " << strerror(errno) << "\n";
+ return false;
+ }
+ if (!android::base::ReadFully(image_fd, image_buffer, bytes)) {
+ std::cerr << "read: " << image_buffer << ": " << strerror(errno) << "\n";
+ return false;
+ }
+ if (!android::base::ReadFully(md_fd, md_buffer, bytes)) {
+ std::cerr << "read: " << md_buffer << ": " << strerror(errno) << "\n";
+ return false;
+ }
+ if (memcmp(canonical_buffer, image_buffer, bytes)) {
+ std::cerr << "canonical and image differ at block " << blockno << "\n";
+ return false;
+ }
+ if (memcmp(canonical_buffer, md_buffer, bytes)) {
+ std::cerr << "canonical and image differ at block " << blockno << "\n";
+ return false;
+ }
+
+ remaining -= bytes;
+ blockno++;
+ }
+
+ std::cout << "Images all match.\n";
+ return true;
+}
+
+} // namespace snapshot
+} // namespace android
+
+int main(int argc, char** argv) {
+ android::snapshot::PowerTest test;
+
+ if (!test.Run(argc, argv)) {
+ std::cerr << "Unexpected error running test." << std::endl;
+ return 1;
+ }
+ fflush(stdout);
+ return 0;
+}
diff --git a/fs_mgr/libsnapshot/return.cpp b/fs_mgr/libsnapshot/return.cpp
index 6559c12..cc64af5 100644
--- a/fs_mgr/libsnapshot/return.cpp
+++ b/fs_mgr/libsnapshot/return.cpp
@@ -24,8 +24,6 @@
switch (error_code()) {
case ErrorCode::ERROR:
return "Error";
- case ErrorCode::NEEDS_REBOOT:
- return "Retry after reboot";
case ErrorCode::SUCCESS:
[[fallthrough]];
case ErrorCode::NO_SPACE:
diff --git a/fs_mgr/libsnapshot/run_power_test.sh b/fs_mgr/libsnapshot/run_power_test.sh
new file mode 100755
index 0000000..dc03dc9
--- /dev/null
+++ b/fs_mgr/libsnapshot/run_power_test.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+
+set -e
+
+if [ -z "$FAIL_RATE" ]; then
+ FAIL_RATE=5.0
+fi
+if [ ! -z "$ANDROID_SERIAL" ]; then
+ DEVICE_ARGS=-s $ANDROID_SERIAL
+else
+ DEVICE_ARGS=
+fi
+
+TEST_BIN=/data/nativetest64/snapshot_power_test/snapshot_power_test
+
+while :
+do
+ adb $DEVICE_ARGS wait-for-device
+ adb $DEVICE_ARGS root
+ adb $DEVICE_ARGS shell rm $TEST_BIN
+ adb $DEVICE_ARGS sync data
+ set +e
+ output=$(adb $DEVICE_ARGS shell $TEST_BIN merge $FAIL_RATE 2>&1)
+ set -e
+ if [[ "$output" == *"Merge completed"* ]]; then
+ echo "Merge completed."
+ break
+ fi
+ if [[ "$output" == *"Unexpected error"* ]]; then
+ echo "Unexpected error."
+ exit 1
+ fi
+done
+
+adb $DEVICE_ARGS shell $TEST_BIN check $1
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 187f24c..5909cff 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -21,7 +21,6 @@
#include <sys/unistd.h>
#include <optional>
-#include <sstream>
#include <thread>
#include <unordered_set>
@@ -38,15 +37,11 @@
#include <libfiemap/image_manager.h>
#include <liblp/liblp.h>
-#ifdef LIBSNAPSHOT_USE_CALLSTACK
-#include <utils/CallStack.h>
-#endif
-
#include <android/snapshot/snapshot.pb.h>
+#include <libsnapshot/snapshot_stats.h>
#include "device_info.h"
#include "partition_cow_creator.h"
#include "snapshot_metadata_updater.h"
-#include "snapshot_stats.h"
#include "utility.h"
namespace android {
@@ -81,6 +76,7 @@
using namespace std::string_literals;
static constexpr char kBootIndicatorPath[] = "/metadata/ota/snapshot-boot";
+static constexpr char kRollbackIndicatorPath[] = "/metadata/ota/rollback-indicator";
static constexpr auto kUpdateStateCheckInterval = 2s;
// Note: IImageManager is an incomplete type in the header, so the default
@@ -219,37 +215,30 @@
return true;
}
-bool SnapshotManager::RemoveAllUpdateState(LockedFile* lock) {
- LOG(INFO) << "Removing all update state.";
-
-#ifdef LIBSNAPSHOT_USE_CALLSTACK
- LOG(WARNING) << "Logging stack; see b/148818798.";
- // Do not use CallStack's log functions because snapshotctl relies on
- // android-base/logging to save log to files.
- // TODO(b/148818798): remove this before we ship.
- CallStack callstack;
- callstack.update();
- auto callstack_str = callstack.toString();
- LOG(WARNING) << callstack_str.c_str();
- std::stringstream path;
- path << "/data/misc/snapshotctl_log/libsnapshot." << Now() << ".log";
- std::string path_str = path.str();
- android::base::WriteStringToFile(callstack_str.c_str(), path_str);
- if (chmod(path_str.c_str(), 0644) == -1) {
- PLOG(WARNING) << "Unable to chmod 0644 "
- << ", file maybe dropped from bugreport:" << path_str;
+bool SnapshotManager::RemoveAllUpdateState(LockedFile* lock, const std::function<bool()>& prolog) {
+ if (prolog && !prolog()) {
+ LOG(WARNING) << "Can't RemoveAllUpdateState: prolog failed.";
+ return false;
}
-#endif
+
+ LOG(INFO) << "Removing all update state.";
if (!RemoveAllSnapshots(lock)) {
LOG(ERROR) << "Could not remove all snapshots";
return false;
}
- // It's okay if these fail - first-stage init performs a deeper check after
+ // It's okay if these fail:
+ // - For SnapshotBoot and Rollback, first-stage init performs a deeper check after
// reading the indicator file, so it's not a problem if it still exists
// after the update completes.
- std::vector<std::string> files = {GetSnapshotBootIndicatorPath(), GetRollbackIndicatorPath()};
+ // - For ForwardMerge, FinishedSnapshotWrites asserts that the existence of the indicator
+ // matches the incoming update.
+ std::vector<std::string> files = {
+ GetSnapshotBootIndicatorPath(),
+ GetRollbackIndicatorPath(),
+ GetForwardMergeIndicatorPath(),
+ };
for (const auto& file : files) {
RemoveFileIfExists(file);
}
@@ -259,7 +248,7 @@
return WriteUpdateState(lock, UpdateState::None);
}
-bool SnapshotManager::FinishedSnapshotWrites() {
+bool SnapshotManager::FinishedSnapshotWrites(bool wipe) {
auto lock = LockExclusive();
if (!lock) return false;
@@ -279,6 +268,10 @@
return false;
}
+ if (!UpdateForwardMergeIndicator(wipe)) {
+ return false;
+ }
+
// This file is written on boot to detect whether a rollback occurred. It
// MUST NOT exist before rebooting, otherwise, we're at risk of deleting
// snapshots too early.
@@ -562,7 +555,7 @@
return true;
}
-bool SnapshotManager::InitiateMerge() {
+bool SnapshotManager::InitiateMerge(uint64_t* cow_file_size) {
auto lock = LockExclusive();
if (!lock) return false;
@@ -584,8 +577,16 @@
return false;
}
+ auto other_suffix = device_->GetOtherSlotSuffix();
+
auto& dm = DeviceMapper::Instance();
for (const auto& snapshot : snapshots) {
+ if (android::base::EndsWith(snapshot, other_suffix)) {
+ // Allow the merge to continue, but log this unexpected case.
+ LOG(ERROR) << "Unexpected snapshot found during merge: " << snapshot;
+ continue;
+ }
+
// The device has to be mapped, since everything should be merged at
// the same time. This is a fairly serious error. We could forcefully
// map everything here, but it should have been mapped during first-
@@ -617,6 +618,7 @@
}
}
+ uint64_t total_cow_file_size = 0;
DmTargetSnapshot::Status initial_target_values = {};
for (const auto& snapshot : snapshots) {
DmTargetSnapshot::Status current_status;
@@ -626,6 +628,16 @@
initial_target_values.sectors_allocated += current_status.sectors_allocated;
initial_target_values.total_sectors += current_status.total_sectors;
initial_target_values.metadata_sectors += current_status.metadata_sectors;
+
+ SnapshotStatus snapshot_status;
+ if (!ReadSnapshotStatus(lock.get(), snapshot, &snapshot_status)) {
+ return false;
+ }
+ total_cow_file_size += snapshot_status.cow_file_size();
+ }
+
+ if (cow_file_size) {
+ *cow_file_size = total_cow_file_size;
}
SnapshotUpdateStatus initial_status;
@@ -789,9 +801,10 @@
// Note that when a merge fails, we will *always* try again to complete the
// merge each time the device boots. There is no harm in doing so, and if
// the problem was transient, we might manage to get a new outcome.
-UpdateState SnapshotManager::ProcessUpdateState(const std::function<void()>& callback) {
+UpdateState SnapshotManager::ProcessUpdateState(const std::function<bool()>& callback,
+ const std::function<bool()>& before_cancel) {
while (true) {
- UpdateState state = CheckMergeState();
+ UpdateState state = CheckMergeState(before_cancel);
if (state == UpdateState::MergeFailed) {
AcknowledgeMergeFailure();
}
@@ -801,8 +814,8 @@
return state;
}
- if (callback) {
- callback();
+ if (callback && !callback()) {
+ return state;
}
// This wait is not super time sensitive, so we have a relatively
@@ -811,24 +824,27 @@
}
}
-UpdateState SnapshotManager::CheckMergeState() {
+UpdateState SnapshotManager::CheckMergeState(const std::function<bool()>& before_cancel) {
auto lock = LockExclusive();
if (!lock) {
return UpdateState::MergeFailed;
}
- UpdateState state = CheckMergeState(lock.get());
+ UpdateState state = CheckMergeState(lock.get(), before_cancel);
if (state == UpdateState::MergeCompleted) {
// Do this inside the same lock. Failures get acknowledged without the
// lock, because flock() might have failed.
AcknowledgeMergeSuccess(lock.get());
} else if (state == UpdateState::Cancelled) {
- RemoveAllUpdateState(lock.get());
+ if (!RemoveAllUpdateState(lock.get(), before_cancel)) {
+ return ReadSnapshotUpdateStatus(lock.get()).state();
+ }
}
return state;
}
-UpdateState SnapshotManager::CheckMergeState(LockedFile* lock) {
+UpdateState SnapshotManager::CheckMergeState(LockedFile* lock,
+ const std::function<bool()>& before_cancel) {
UpdateState state = ReadUpdateState(lock);
switch (state) {
case UpdateState::None:
@@ -849,7 +865,7 @@
// This is an edge case. Normally cancelled updates are detected
// via the merge poll below, but if we never started a merge, we
// need to also check here.
- if (HandleCancelledUpdate(lock)) {
+ if (HandleCancelledUpdate(lock, before_cancel)) {
return UpdateState::Cancelled;
}
return state;
@@ -1003,10 +1019,23 @@
}
std::string SnapshotManager::GetRollbackIndicatorPath() {
- return metadata_dir_ + "/rollback-indicator";
+ return metadata_dir_ + "/" + android::base::Basename(kRollbackIndicatorPath);
+}
+
+std::string SnapshotManager::GetForwardMergeIndicatorPath() {
+ return metadata_dir_ + "/allow-forward-merge";
}
void SnapshotManager::AcknowledgeMergeSuccess(LockedFile* lock) {
+ // It's not possible to remove update state in recovery, so write an
+ // indicator that cleanup is needed on reboot. If a factory data reset
+ // was requested, it doesn't matter, everything will get wiped anyway.
+ // To make testing easier we consider a /data wipe as cleaned up.
+ if (device_->IsRecovery() && !in_factory_data_reset_) {
+ WriteUpdateState(lock, UpdateState::MergeCompleted);
+ return;
+ }
+
RemoveAllUpdateState(lock);
}
@@ -1169,7 +1198,8 @@
return true;
}
-bool SnapshotManager::HandleCancelledUpdate(LockedFile* lock) {
+bool SnapshotManager::HandleCancelledUpdate(LockedFile* lock,
+ const std::function<bool()>& before_cancel) {
auto slot = GetCurrentSlot();
if (slot == Slot::Unknown) {
return false;
@@ -1177,15 +1207,30 @@
// If all snapshots were reflashed, then cancel the entire update.
if (AreAllSnapshotsCancelled(lock)) {
- RemoveAllUpdateState(lock);
- return true;
+ LOG(WARNING) << "Detected re-flashing, cancelling unverified update.";
+ return RemoveAllUpdateState(lock, before_cancel);
}
- // This unverified update might be rolled back, or it might not (b/147347110
- // comment #77). Take no action, as update_engine is responsible for deciding
- // whether to cancel.
- LOG(ERROR) << "Update state is being processed before reboot, taking no action.";
- return false;
+ // If update has been rolled back, then cancel the entire update.
+ // Client (update_engine) is responsible for doing additional cleanup work on its own states
+ // when ProcessUpdateState() returns UpdateState::Cancelled.
+ auto current_slot = GetCurrentSlot();
+ if (current_slot != Slot::Source) {
+ LOG(INFO) << "Update state is being processed while booting at " << current_slot
+ << " slot, taking no action.";
+ return false;
+ }
+
+ // current_slot == Source. Attempt to detect rollbacks.
+ if (access(GetRollbackIndicatorPath().c_str(), F_OK) != 0) {
+ // This unverified update is not attempted. Take no action.
+ PLOG(INFO) << "Rollback indicator not detected. "
+ << "Update state is being processed before reboot, taking no action.";
+ return false;
+ }
+
+ LOG(WARNING) << "Detected rollback, cancelling unverified update.";
+ return RemoveAllUpdateState(lock, before_cancel);
}
std::unique_ptr<LpMetadata> SnapshotManager::ReadCurrentMetadata() {
@@ -1219,6 +1264,28 @@
return true;
}
+ std::map<std::string, bool> flashing_status;
+
+ if (!GetSnapshotFlashingStatus(lock, snapshots, &flashing_status)) {
+ LOG(WARNING) << "Failed to determine whether partitions have been flashed. Not"
+ << "removing update states.";
+ return false;
+ }
+
+ bool all_snapshots_cancelled = std::all_of(flashing_status.begin(), flashing_status.end(),
+ [](const auto& pair) { return pair.second; });
+
+ if (all_snapshots_cancelled) {
+ LOG(WARNING) << "All partitions are re-flashed after update, removing all update states.";
+ }
+ return all_snapshots_cancelled;
+}
+
+bool SnapshotManager::GetSnapshotFlashingStatus(LockedFile* lock,
+ const std::vector<std::string>& snapshots,
+ std::map<std::string, bool>* out) {
+ CHECK(lock);
+
auto source_slot_suffix = ReadUpdateSourceSlotSuffix();
if (source_slot_suffix.empty()) {
return false;
@@ -1244,20 +1311,17 @@
return false;
}
- bool all_snapshots_cancelled = true;
for (const auto& snapshot_name : snapshots) {
if (GetMetadataPartitionState(*metadata, snapshot_name) ==
MetadataPartitionState::Updated) {
- all_snapshots_cancelled = false;
- continue;
+ out->emplace(snapshot_name, false);
+ } else {
+ // Delete snapshots for partitions that are re-flashed after the update.
+ LOG(WARNING) << "Detected re-flashing of partition " << snapshot_name << ".";
+ out->emplace(snapshot_name, true);
}
- // Delete snapshots for partitions that are re-flashed after the update.
- LOG(WARNING) << "Detected re-flashing of partition " << snapshot_name << ".";
}
- if (all_snapshots_cancelled) {
- LOG(WARNING) << "All partitions are re-flashed after update, removing all update states.";
- }
- return all_snapshots_cancelled;
+ return true;
}
bool SnapshotManager::RemoveAllSnapshots(LockedFile* lock) {
@@ -1267,10 +1331,38 @@
return false;
}
+ std::map<std::string, bool> flashing_status;
+ if (!GetSnapshotFlashingStatus(lock, snapshots, &flashing_status)) {
+ LOG(WARNING) << "Failed to get flashing status";
+ }
+
+ auto current_slot = GetCurrentSlot();
bool ok = true;
bool has_mapped_cow_images = false;
for (const auto& name : snapshots) {
- if (!UnmapPartitionWithSnapshot(lock, name) || !DeleteSnapshot(lock, name)) {
+ // If booting off source slot, it is okay to unmap and delete all the snapshots.
+ // If boot indicator is missing, update state is None or Initiated, so
+ // it is also okay to unmap and delete all the snapshots.
+ // If booting off target slot,
+ // - should not unmap because:
+ // - In Android mode, snapshots are not mapped, but
+ // filesystems are mounting off dm-linear targets directly.
+ // - In recovery mode, assume nothing is mapped, so it is optional to unmap.
+ // - If partition is flashed or unknown, it is okay to delete snapshots.
+ // Otherwise (UPDATED flag), only delete snapshots if they are not mapped
+ // as dm-snapshot (for example, after merge completes).
+ bool should_unmap = current_slot != Slot::Target;
+ bool should_delete = ShouldDeleteSnapshot(lock, flashing_status, current_slot, name);
+
+ bool partition_ok = true;
+ if (should_unmap && !UnmapPartitionWithSnapshot(lock, name)) {
+ partition_ok = false;
+ }
+ if (partition_ok && should_delete && !DeleteSnapshot(lock, name)) {
+ partition_ok = false;
+ }
+
+ if (!partition_ok) {
// Remember whether or not we were able to unmap the cow image.
auto cow_image_device = GetCowImageDeviceName(name);
has_mapped_cow_images |=
@@ -1293,6 +1385,34 @@
return ok;
}
+// See comments in RemoveAllSnapshots().
+bool SnapshotManager::ShouldDeleteSnapshot(LockedFile* lock,
+ const std::map<std::string, bool>& flashing_status,
+ Slot current_slot, const std::string& name) {
+ if (current_slot != Slot::Target) {
+ return true;
+ }
+ auto it = flashing_status.find(name);
+ if (it == flashing_status.end()) {
+ LOG(WARNING) << "Can't determine flashing status for " << name;
+ return true;
+ }
+ if (it->second) {
+ // partition flashed, okay to delete obsolete snapshots
+ return true;
+ }
+ // partition updated, only delete if not dm-snapshot
+ SnapshotStatus status;
+ if (!ReadSnapshotStatus(lock, name, &status)) {
+ LOG(WARNING) << "Unable to read snapshot status for " << name
+ << ", guessing snapshot device name";
+ auto extra_name = GetSnapshotExtraDeviceName(name);
+ return !IsSnapshotDevice(name) && !IsSnapshotDevice(extra_name);
+ }
+ auto dm_name = GetSnapshotDeviceName(name, status);
+ return !IsSnapshotDevice(dm_name);
+}
+
UpdateState SnapshotManager::GetUpdateState(double* progress) {
// If we've never started an update, the state file won't exist.
auto state_file = GetStateFilePath();
@@ -1369,6 +1489,10 @@
return access(kBootIndicatorPath, F_OK) == 0;
}
+std::string SnapshotManager::GetGlobalRollbackIndicatorPath() {
+ return kRollbackIndicatorPath;
+}
+
bool SnapshotManager::NeedSnapshotsInFirstStageMount() {
// If we fail to read, we'll wind up using CreateLogicalPartitions, which
// will create devices that look like the old slot, except with extra
@@ -1379,12 +1503,14 @@
auto slot = GetCurrentSlot();
if (slot != Slot::Target) {
- if (slot == Slot::Source && !device_->IsRecovery()) {
+ if (slot == Slot::Source) {
// Device is rebooting into the original slot, so mark this as a
// rollback.
auto path = GetRollbackIndicatorPath();
if (!android::base::WriteStringToFile("1", path)) {
PLOG(ERROR) << "Unable to write rollback indicator: " << path;
+ } else {
+ LOG(INFO) << "Rollback detected, writing rollback indicator to " << path;
}
}
LOG(INFO) << "Not booting from new slot. Will not mount snapshots.";
@@ -1576,7 +1702,7 @@
return false;
}
std::string cow_device;
- if (!dm.GetDeviceString(cow_name, &cow_device)) {
+ if (!GetMappedImageDeviceStringOrPath(cow_name, &cow_device)) {
LOG(ERROR) << "Could not determine major/minor for: " << cow_name;
return false;
}
@@ -1673,7 +1799,7 @@
// If the COW image exists, append it as the last extent.
if (snapshot_status.cow_file_size() > 0) {
std::string cow_image_device;
- if (!dm.GetDeviceString(cow_image_name, &cow_image_device)) {
+ if (!GetMappedImageDeviceStringOrPath(cow_image_name, &cow_image_device)) {
LOG(ERROR) << "Cannot determine major/minor for: " << cow_image_name;
return false;
}
@@ -2249,7 +2375,6 @@
const std::map<std::string, SnapshotStatus>& all_snapshot_status) {
CHECK(lock);
- auto& dm = DeviceMapper::Instance();
CreateLogicalPartitionParams cow_params{
.block_device = LP_METADATA_DEFAULT_PARTITION_NAME,
.metadata = exported_target_metadata,
@@ -2274,7 +2399,7 @@
}
std::string cow_path;
- if (!dm.GetDmDevicePathByName(cow_name, &cow_path)) {
+ if (!images_->GetMappedImageDevice(cow_name, &cow_path)) {
LOG(ERROR) << "Cannot determine path for " << cow_name;
return Return::Error();
}
@@ -2355,6 +2480,9 @@
ss << "Rollback indicator: "
<< (access(GetRollbackIndicatorPath().c_str(), F_OK) == 0 ? "exists" : strerror(errno))
<< std::endl;
+ ss << "Forward merge indicator: "
+ << (access(GetForwardMergeIndicatorPath().c_str(), F_OK) == 0 ? "exists" : strerror(errno))
+ << std::endl;
bool ok = true;
std::vector<std::string> snapshots;
@@ -2391,91 +2519,6 @@
return AutoUnmountDevice::New(device_->GetMetadataDir());
}
-UpdateState SnapshotManager::InitiateMergeAndWait(SnapshotMergeReport* stats_report) {
- {
- auto lock = LockExclusive();
- // Sync update state from file with bootloader.
- if (!WriteUpdateState(lock.get(), ReadUpdateState(lock.get()))) {
- LOG(WARNING) << "Unable to sync write update state, fastboot may "
- << "reject / accept wipes incorrectly!";
- }
- }
-
- SnapshotMergeStats merge_stats(*this);
-
- unsigned int last_progress = 0;
- auto callback = [&]() -> void {
- double progress;
- GetUpdateState(&progress);
- if (last_progress < static_cast<unsigned int>(progress)) {
- last_progress = progress;
- LOG(INFO) << "Waiting for merge to complete: " << last_progress << "%.";
- }
- };
-
- LOG(INFO) << "Waiting for any previous merge request to complete. "
- << "This can take up to several minutes.";
- merge_stats.Resume();
- auto state = ProcessUpdateState(callback);
- merge_stats.set_state(state);
- if (state == UpdateState::None) {
- LOG(INFO) << "Can't find any snapshot to merge.";
- return state;
- }
- if (state == UpdateState::Unverified) {
- if (GetCurrentSlot() != Slot::Target) {
- LOG(INFO) << "Cannot merge until device reboots.";
- return state;
- }
-
- // This is the first snapshot merge that is requested after OTA. We can
- // initialize the merge duration statistics.
- merge_stats.Start();
-
- if (!InitiateMerge()) {
- LOG(ERROR) << "Failed to initiate merge.";
- return state;
- }
- // All other states can be handled by ProcessUpdateState.
- LOG(INFO) << "Waiting for merge to complete. This can take up to several minutes.";
- last_progress = 0;
- state = ProcessUpdateState(callback);
- merge_stats.set_state(state);
- }
-
- LOG(INFO) << "Merge finished with state \"" << state << "\".";
- if (stats_report) {
- *stats_report = merge_stats.GetReport();
- }
- return state;
-}
-
-Return SnapshotManager::WaitForMerge() {
- LOG(INFO) << "Waiting for any previous merge request to complete. "
- << "This can take up to several minutes.";
- while (true) {
- auto state = ProcessUpdateState();
- if (state == UpdateState::Unverified && GetCurrentSlot() == Slot::Target) {
- LOG(INFO) << "Wait for merge to be initiated.";
- std::this_thread::sleep_for(kUpdateStateCheckInterval);
- continue;
- }
- LOG(INFO) << "Wait for merge exits with state " << state;
- switch (state) {
- case UpdateState::None:
- [[fallthrough]];
- case UpdateState::MergeCompleted:
- [[fallthrough]];
- case UpdateState::Cancelled:
- return Return::Ok();
- case UpdateState::MergeNeedsReboot:
- return Return::NeedsReboot();
- default:
- return Return::Error();
- }
- }
-}
-
bool SnapshotManager::HandleImminentDataWipe(const std::function<void()>& callback) {
if (!device_->IsRecovery()) {
LOG(ERROR) << "Data wipes are only allowed in recovery.";
@@ -2506,6 +2549,62 @@
return false;
}
+ auto process_callback = [&]() -> bool {
+ if (callback) {
+ callback();
+ }
+ return true;
+ };
+
+ in_factory_data_reset_ = true;
+ bool ok = ProcessUpdateStateOnDataWipe(true /* allow_forward_merge */, process_callback);
+ in_factory_data_reset_ = false;
+
+ if (!ok) {
+ return false;
+ }
+
+ // Nothing should be depending on partitions now, so unmap them all.
+ if (!UnmapAllPartitions()) {
+ LOG(ERROR) << "Unable to unmap all partitions; fastboot may fail to flash.";
+ }
+ return true;
+}
+
+bool SnapshotManager::FinishMergeInRecovery() {
+ if (!device_->IsRecovery()) {
+ LOG(ERROR) << "Data wipes are only allowed in recovery.";
+ return false;
+ }
+
+ auto mount = EnsureMetadataMounted();
+ if (!mount || !mount->HasDevice()) {
+ return false;
+ }
+
+ auto slot_number = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
+ auto super_path = device_->GetSuperDevice(slot_number);
+ if (!CreateLogicalAndSnapshotPartitions(super_path)) {
+ LOG(ERROR) << "Unable to map partitions to complete merge.";
+ return false;
+ }
+
+ UpdateState state = ProcessUpdateState();
+ if (state != UpdateState::MergeCompleted) {
+ LOG(ERROR) << "Merge returned unexpected status: " << state;
+ return false;
+ }
+
+ // Nothing should be depending on partitions now, so unmap them all.
+ if (!UnmapAllPartitions()) {
+ LOG(ERROR) << "Unable to unmap all partitions; fastboot may fail to flash.";
+ }
+ return true;
+}
+
+bool SnapshotManager::ProcessUpdateStateOnDataWipe(bool allow_forward_merge,
+ const std::function<bool()>& callback) {
+ auto slot_number = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
UpdateState state = ProcessUpdateState(callback);
LOG(INFO) << "Update state in recovery: " << state;
switch (state) {
@@ -2513,7 +2612,12 @@
LOG(ERROR) << "Unrecoverable merge failure detected.";
return false;
case UpdateState::Unverified: {
- // If an OTA was just applied but has not yet started merging, we
+ // If an OTA was just applied but has not yet started merging:
+ //
+ // - if forward merge is allowed, initiate merge and call
+ // ProcessUpdateState again.
+ //
+ // - if forward merge is not allowed, we
// have no choice but to revert slots, because the current slot will
// immediately become unbootable. Rather than wait for the device
// to reboot N times until a rollback, we proactively disable the
@@ -2523,8 +2627,17 @@
// as an error here.
auto slot = GetCurrentSlot();
if (slot == Slot::Target) {
+ if (allow_forward_merge &&
+ access(GetForwardMergeIndicatorPath().c_str(), F_OK) == 0) {
+ LOG(INFO) << "Forward merge allowed, initiating merge now.";
+ return InitiateMerge() &&
+ ProcessUpdateStateOnDataWipe(false /* allow_forward_merge */, callback);
+ }
+
LOG(ERROR) << "Reverting to old slot since update will be deleted.";
device_->SetSlotAsUnbootable(slot_number);
+ } else {
+ LOG(INFO) << "Booting from " << slot << " slot, no action is taken.";
}
break;
}
@@ -2536,11 +2649,6 @@
default:
break;
}
-
- // Nothing should be depending on partitions now, so unmap them all.
- if (!UnmapAllPartitions()) {
- LOG(ERROR) << "Unable to unmap all partitions; fastboot may fail to flash.";
- }
return true;
}
@@ -2586,6 +2694,20 @@
LOG(ERROR) << "Couldn't mount Metadata.";
return CreateResult::NOT_CREATED;
}
+ return RecoveryCreateSnapshotDevices(mount);
+}
+
+CreateResult SnapshotManager::RecoveryCreateSnapshotDevices(
+ const std::unique_ptr<AutoDevice>& metadata_device) {
+ if (!device_->IsRecovery()) {
+ LOG(ERROR) << __func__ << " is only allowed in recovery.";
+ return CreateResult::NOT_CREATED;
+ }
+
+ if (metadata_device == nullptr || !metadata_device->HasDevice()) {
+ LOG(ERROR) << "Metadata not mounted.";
+ return CreateResult::NOT_CREATED;
+ }
auto state_file = GetStateFilePath();
if (access(state_file.c_str(), F_OK) != 0 && errno == ENOENT) {
@@ -2607,5 +2729,47 @@
return CreateResult::CREATED;
}
+bool SnapshotManager::UpdateForwardMergeIndicator(bool wipe) {
+ auto path = GetForwardMergeIndicatorPath();
+
+ if (!wipe) {
+ LOG(INFO) << "Wipe is not scheduled. Deleting forward merge indicator.";
+ return RemoveFileIfExists(path);
+ }
+
+ // TODO(b/152094219): Don't forward merge if no CoW file is allocated.
+
+ LOG(INFO) << "Wipe will be scheduled. Allowing forward merge of snapshots.";
+ if (!android::base::WriteStringToFile("1", path)) {
+ PLOG(ERROR) << "Unable to write forward merge indicator: " << path;
+ return false;
+ }
+
+ return true;
+}
+
+ISnapshotMergeStats* SnapshotManager::GetSnapshotMergeStatsInstance() {
+ return SnapshotMergeStats::GetInstance(*this);
+}
+
+bool SnapshotManager::GetMappedImageDeviceStringOrPath(const std::string& device_name,
+ std::string* device_string_or_mapped_path) {
+ auto& dm = DeviceMapper::Instance();
+ // Try getting the device string if it is a device mapper device.
+ if (dm.GetState(device_name) != DmDeviceState::INVALID) {
+ return dm.GetDeviceString(device_name, device_string_or_mapped_path);
+ }
+
+ // Otherwise, get path from IImageManager.
+ if (!images_->GetMappedImageDevice(device_name, device_string_or_mapped_path)) {
+ return false;
+ }
+
+ LOG(WARNING) << "Calling GetMappedImageDevice with local image manager; device "
+ << (device_string_or_mapped_path ? *device_string_or_mapped_path : "(nullptr)")
+ << "may not be available in first stage init! ";
+ return true;
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz.cpp b/fs_mgr/libsnapshot/snapshot_fuzz.cpp
new file mode 100644
index 0000000..5b145c3
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshot_fuzz.cpp
@@ -0,0 +1,352 @@
+// 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 <android-base/properties.h>
+#include <android-base/result.h>
+#include <gtest/gtest.h>
+#include <src/libfuzzer/libfuzzer_macro.h>
+#include <storage_literals/storage_literals.h>
+
+#include "fuzz_utils.h"
+#include "snapshot_fuzz_utils.h"
+
+using android::base::Error;
+using android::base::GetBoolProperty;
+using android::base::LogId;
+using android::base::LogSeverity;
+using android::base::ReadFileToString;
+using android::base::Result;
+using android::base::SetLogger;
+using android::base::StderrLogger;
+using android::base::StdioLogger;
+using android::fs_mgr::CreateLogicalPartitionParams;
+using android::fuzz::CheckedCast;
+using android::snapshot::SnapshotFuzzData;
+using android::snapshot::SnapshotFuzzEnv;
+using chromeos_update_engine::DeltaArchiveManifest;
+using google::protobuf::FieldDescriptor;
+using google::protobuf::Message;
+using google::protobuf::RepeatedPtrField;
+
+// 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 {
+
+const SnapshotFuzzData* current_data = nullptr;
+const SnapshotTestModule* current_module = nullptr;
+
+SnapshotFuzzEnv* GetSnapshotFuzzEnv();
+
+FUZZ_CLASS(ISnapshotManager, SnapshotManagerAction);
+
+using ProcessUpdateStateArgs = SnapshotManagerAction::Proto::ProcessUpdateStateArgs;
+using CreateLogicalAndSnapshotPartitionsArgs =
+ SnapshotManagerAction::Proto::CreateLogicalAndSnapshotPartitionsArgs;
+using RecoveryCreateSnapshotDevicesArgs =
+ SnapshotManagerAction::Proto::RecoveryCreateSnapshotDevicesArgs;
+
+FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, BeginUpdate);
+FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, CancelUpdate);
+FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, InitiateMerge);
+FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, NeedSnapshotsInFirstStageMount);
+FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, RecoveryCreateSnapshotDevices);
+FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, EnsureMetadataMounted);
+FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, GetSnapshotMergeStatsInstance);
+
+#define SNAPSHOT_FUZZ_FUNCTION(FunctionName, ReturnType, ...) \
+ FUZZ_FUNCTION(SnapshotManagerAction, FunctionName, ReturnType, ISnapshotManager* snapshot, \
+ ##__VA_ARGS__)
+
+SNAPSHOT_FUZZ_FUNCTION(FinishedSnapshotWrites, bool, bool wipe) {
+ return snapshot->FinishedSnapshotWrites(wipe);
+}
+
+SNAPSHOT_FUZZ_FUNCTION(ProcessUpdateState, bool, const ProcessUpdateStateArgs& args) {
+ std::function<bool()> before_cancel;
+ if (args.has_before_cancel()) {
+ before_cancel = [&]() { return args.fail_before_cancel(); };
+ }
+ return snapshot->ProcessUpdateState({}, before_cancel);
+}
+
+SNAPSHOT_FUZZ_FUNCTION(GetUpdateState, UpdateState, bool has_progress_arg) {
+ double progress;
+ return snapshot->GetUpdateState(has_progress_arg ? &progress : nullptr);
+}
+
+SNAPSHOT_FUZZ_FUNCTION(HandleImminentDataWipe, bool, bool has_callback) {
+ std::function<void()> callback;
+ if (has_callback) {
+ callback = []() {};
+ }
+ return snapshot->HandleImminentDataWipe(callback);
+}
+
+SNAPSHOT_FUZZ_FUNCTION(Dump, bool) {
+ std::stringstream ss;
+ return snapshot->Dump(ss);
+}
+
+SNAPSHOT_FUZZ_FUNCTION(CreateUpdateSnapshots, bool, const DeltaArchiveManifest& manifest) {
+ return snapshot->CreateUpdateSnapshots(manifest);
+}
+
+SNAPSHOT_FUZZ_FUNCTION(UnmapUpdateSnapshot, bool, const std::string& name) {
+ return snapshot->UnmapUpdateSnapshot(name);
+}
+
+SNAPSHOT_FUZZ_FUNCTION(CreateLogicalAndSnapshotPartitions, bool,
+ const CreateLogicalAndSnapshotPartitionsArgs& args) {
+ const std::string* super;
+ if (args.use_correct_super()) {
+ super = &GetSnapshotFuzzEnv()->super();
+ } else {
+ super = &args.super();
+ }
+ return snapshot->CreateLogicalAndSnapshotPartitions(
+ *super, std::chrono::milliseconds(args.timeout_millis()));
+}
+
+SNAPSHOT_FUZZ_FUNCTION(RecoveryCreateSnapshotDevicesWithMetadata, CreateResult,
+ const RecoveryCreateSnapshotDevicesArgs& args) {
+ std::unique_ptr<AutoDevice> device;
+ if (args.has_metadata_device_object()) {
+ device = std::make_unique<DummyAutoDevice>(args.metadata_mounted());
+ }
+ return snapshot->RecoveryCreateSnapshotDevices(device);
+}
+
+SNAPSHOT_FUZZ_FUNCTION(MapUpdateSnapshot, bool,
+ const CreateLogicalPartitionParamsProto& params_proto) {
+ auto partition_opener = std::make_unique<TestPartitionOpener>(GetSnapshotFuzzEnv()->super());
+ CreateLogicalPartitionParams params;
+ if (params_proto.use_correct_super()) {
+ params.block_device = GetSnapshotFuzzEnv()->super();
+ } else {
+ params.block_device = params_proto.block_device();
+ }
+ if (params_proto.has_metadata_slot()) {
+ params.metadata_slot = params_proto.metadata_slot();
+ }
+ params.partition_name = params_proto.partition_name();
+ params.force_writable = params_proto.force_writable();
+ params.timeout_ms = std::chrono::milliseconds(params_proto.timeout_millis());
+ params.device_name = params_proto.device_name();
+ params.partition_opener = partition_opener.get();
+ std::string path;
+ return snapshot->MapUpdateSnapshot(params, &path);
+}
+
+SNAPSHOT_FUZZ_FUNCTION(SwitchSlot, void) {
+ (void)snapshot;
+ CHECK(current_module != nullptr);
+ CHECK(current_module->device_info != nullptr);
+ current_module->device_info->SwitchSlot();
+}
+
+// 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);
+
+ // If test fails by a LOG(FATAL) or CHECK(), log the corpus. If it abort()'s, there's
+ // nothing else we can do.
+ StderrLogger(logid, severity, tag, __FILE__, __LINE__,
+ "Attempting to dump current corpus:");
+ if (current_data == nullptr) {
+ StderrLogger(logid, severity, tag, __FILE__, __LINE__, "Current corpus is nullptr.");
+ } else {
+ std::string content;
+ if (!google::protobuf::TextFormat::PrintToString(*current_data, &content)) {
+ StderrLogger(logid, severity, tag, __FILE__, __LINE__,
+ "Failed to print corpus to string.");
+ } else {
+ StderrLogger(logid, severity, tag, __FILE__, __LINE__, content.c_str());
+ }
+ }
+ }
+}
+// Stop logging (except fatal messages) after global initialization. This is only done once.
+int StopLoggingAfterGlobalInit() {
+ (void)GetSnapshotFuzzEnv();
+ [[maybe_unused]] static protobuf_mutator::protobuf::LogSilencer log_silencer;
+ SetLogger(&FatalOnlyLogger);
+ return 0;
+}
+
+SnapshotFuzzEnv* GetSnapshotFuzzEnv() {
+ [[maybe_unused]] static auto allow_logging = AllowLoggingDuringGlobalInit();
+ static SnapshotFuzzEnv env;
+ return &env;
+}
+
+SnapshotTestModule SetUpTest(const SnapshotFuzzData& snapshot_fuzz_data) {
+ current_data = &snapshot_fuzz_data;
+
+ auto env = GetSnapshotFuzzEnv();
+ env->CheckSoftReset();
+
+ auto test_module = env->CheckCreateSnapshotManager(snapshot_fuzz_data);
+ current_module = &test_module;
+ CHECK(test_module.snapshot);
+ return test_module;
+}
+
+void TearDownTest() {
+ current_module = nullptr;
+ current_data = nullptr;
+}
+
+} // namespace android::snapshot
+
+DEFINE_PROTO_FUZZER(const SnapshotFuzzData& snapshot_fuzz_data) {
+ using namespace android::snapshot;
+
+ [[maybe_unused]] static auto stop_logging = StopLoggingAfterGlobalInit();
+ auto test_module = SetUpTest(snapshot_fuzz_data);
+ SnapshotManagerAction::ExecuteAll(test_module.snapshot.get(), snapshot_fuzz_data.actions());
+ TearDownTest();
+}
+
+namespace android::snapshot {
+
+// Work-around to cast a 'void' value to Result<void>.
+template <typename T>
+struct GoodResult {
+ template <typename F>
+ static Result<T> Cast(F&& f) {
+ return f();
+ }
+};
+
+template <>
+struct GoodResult<void> {
+ template <typename F>
+ static Result<void> Cast(F&& f) {
+ f();
+ return {};
+ }
+};
+
+class LibsnapshotFuzzerTest : public ::testing::Test {
+ protected:
+ static void SetUpTestCase() {
+ // Do initialization once.
+ (void)GetSnapshotFuzzEnv();
+ }
+ void SetUp() override {
+ bool is_virtual_ab = GetBoolProperty("ro.virtual_ab.enabled", false);
+ if (!is_virtual_ab) GTEST_SKIP() << "Test only runs on Virtual A/B devices.";
+ }
+ void SetUpFuzzData(const std::string& fn) {
+ auto path = android::base::GetExecutableDirectory() + "/corpus/"s + fn;
+ std::string proto_text;
+ ASSERT_TRUE(ReadFileToString(path, &proto_text));
+ snapshot_fuzz_data_ = std::make_unique<SnapshotFuzzData>();
+ ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(proto_text,
+ snapshot_fuzz_data_.get()));
+ test_module_ = android::snapshot::SetUpTest(*snapshot_fuzz_data_);
+ }
+ void TearDown() override { android::snapshot::TearDownTest(); }
+ template <typename FuzzFunction>
+ Result<typename FuzzFunction::ReturnType> Execute(int action_index) {
+ if (action_index >= snapshot_fuzz_data_->actions_size()) {
+ return Error() << "Index " << action_index << " is out of bounds ("
+ << snapshot_fuzz_data_->actions_size() << " actions in corpus";
+ }
+ const auto& action_proto = snapshot_fuzz_data_->actions(action_index);
+ const auto* field_desc =
+ android::fuzz::GetValueFieldDescriptor<typename FuzzFunction::ActionType>(
+ action_proto);
+ if (field_desc == nullptr) {
+ return Error() << "Action at index " << action_index << " has no value defined.";
+ }
+ if (FuzzFunction::tag != field_desc->number()) {
+ return Error() << "Action at index " << action_index << " is expected to be "
+ << FuzzFunction::name << ", but it is " << field_desc->name()
+ << " in corpus.";
+ }
+ return GoodResult<typename FuzzFunction::ReturnType>::Cast([&]() {
+ return android::fuzz::ActionPerformer<FuzzFunction>::Invoke(test_module_.snapshot.get(),
+ action_proto, field_desc);
+ });
+ }
+
+ std::unique_ptr<SnapshotFuzzData> snapshot_fuzz_data_;
+ SnapshotTestModule test_module_;
+};
+
+#define SNAPSHOT_FUZZ_FN_NAME(name) FUZZ_FUNCTION_CLASS_NAME(SnapshotManagerAction, name)
+
+MATCHER_P(ResultIs, expected, "") {
+ if (!arg.ok()) {
+ *result_listener << arg.error();
+ return false;
+ }
+ *result_listener << "expected: " << expected;
+ return arg.value() == expected;
+}
+
+#define ASSERT_RESULT_TRUE(actual) ASSERT_THAT(actual, ResultIs(true))
+
+// Check that launch_device.txt is executed correctly.
+TEST_F(LibsnapshotFuzzerTest, LaunchDevice) {
+ SetUpFuzzData("launch_device.txt");
+
+ int i = 0;
+ ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(BeginUpdate)>(i++));
+ ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(CreateUpdateSnapshots)>(i++));
+ ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(MapUpdateSnapshot)>(i++)) << "sys_b";
+ ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(MapUpdateSnapshot)>(i++)) << "vnd_b";
+ ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(MapUpdateSnapshot)>(i++)) << "prd_b";
+ ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(FinishedSnapshotWrites)>(i++));
+ ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(UnmapUpdateSnapshot)>(i++)) << "sys_b";
+ ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(UnmapUpdateSnapshot)>(i++)) << "vnd_b";
+ ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(UnmapUpdateSnapshot)>(i++)) << "prd_b";
+ ASSERT_RESULT_OK(Execute<SNAPSHOT_FUZZ_FN_NAME(SwitchSlot)>(i++));
+ ASSERT_EQ("_b", test_module_.device_info->GetSlotSuffix());
+ ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(NeedSnapshotsInFirstStageMount)>(i++));
+ ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(CreateLogicalAndSnapshotPartitions)>(i++));
+ ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(InitiateMerge)>(i++));
+ ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(ProcessUpdateState)>(i++));
+ ASSERT_EQ(i, snapshot_fuzz_data_->actions_size()) << "Not all actions are executed.";
+}
+
+} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp b/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp
new file mode 100644
index 0000000..8926535
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp
@@ -0,0 +1,516 @@
+// 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 <chrono>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <fs_mgr.h>
+#include <libsnapshot/auto_device.h>
+#include <libsnapshot/snapshot.h>
+#include <storage_literals/storage_literals.h>
+
+#include "snapshot_fuzz_utils.h"
+#include "utility.h"
+
+// Prepends the errno string, but it is good enough.
+#ifndef PCHECK
+#define PCHECK(x) CHECK(x) << strerror(errno) << ": "
+#endif
+
+using namespace android::storage_literals;
+using namespace std::chrono_literals;
+using namespace std::string_literals;
+
+using android::base::Basename;
+using android::base::ReadFileToString;
+using android::base::SetProperty;
+using android::base::Split;
+using android::base::StartsWith;
+using android::base::StringPrintf;
+using android::base::unique_fd;
+using android::base::WriteStringToFile;
+using android::dm::DeviceMapper;
+using android::dm::DmTarget;
+using android::dm::LoopControl;
+using android::fiemap::IImageManager;
+using android::fiemap::ImageManager;
+using android::fs_mgr::BlockDeviceInfo;
+using android::fs_mgr::FstabEntry;
+using android::fs_mgr::IPartitionOpener;
+using chromeos_update_engine::DynamicPartitionMetadata;
+
+static const char MNT_DIR[] = "/mnt";
+static const char BLOCK_SYSFS[] = "/sys/block";
+
+static const char FAKE_ROOT_NAME[] = "snapshot_fuzz";
+static const auto SUPER_IMAGE_SIZE = 16_MiB;
+static const auto DATA_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;
+}
+
+std::string GetLinearBaseDeviceString(const DeviceMapper::TargetInfo& target) {
+ if (target.spec.target_type != "linear"s) return {};
+ auto tokens = Split(target.data, " ");
+ CHECK_EQ(2, tokens.size());
+ return tokens[0];
+}
+
+std::vector<std::string> GetSnapshotBaseDeviceStrings(const DeviceMapper::TargetInfo& target) {
+ if (target.spec.target_type != "snapshot"s && target.spec.target_type != "snapshot-merge"s)
+ return {};
+ auto tokens = Split(target.data, " ");
+ CHECK_EQ(4, tokens.size());
+ return {tokens[0], tokens[1]};
+}
+
+bool ShouldDeleteLoopDevice(const std::string& node) {
+ std::string backing_file;
+ if (ReadFileToString(StringPrintf("%s/loop/backing_file", node.data()), &backing_file)) {
+ if (StartsWith(backing_file, std::string(MNT_DIR) + "/" + FAKE_ROOT_NAME)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+std::vector<DeviceMapper::TargetInfo> GetTableInfoIfExists(const std::string& dev_name) {
+ auto& dm = DeviceMapper::Instance();
+ std::vector<DeviceMapper::TargetInfo> table;
+ if (!dm.GetTableInfo(dev_name, &table)) {
+ PCHECK(errno == ENODEV);
+ return {};
+ }
+ return table;
+}
+
+std::set<std::string> GetAllBaseDeviceStrings(const std::string& child_dev) {
+ std::set<std::string> ret;
+ for (const auto& child_target : GetTableInfoIfExists(child_dev)) {
+ auto snapshot_bases = GetSnapshotBaseDeviceStrings(child_target);
+ ret.insert(snapshot_bases.begin(), snapshot_bases.end());
+
+ auto linear_base = GetLinearBaseDeviceString(child_target);
+ if (!linear_base.empty()) {
+ ret.insert(linear_base);
+ }
+ }
+ return ret;
+}
+
+using PropertyList = std::set<std::string>;
+void InsertProperty(const char* key, const char* /*name*/, void* cookie) {
+ reinterpret_cast<PropertyList*>(cookie)->insert(key);
+}
+
+// Attempt to delete all devices that is based on dev_name, including itself.
+void CheckDeleteDeviceMapperTree(const std::string& dev_name, bool known_allow_delete = false,
+ uint64_t depth = 100) {
+ CHECK(depth > 0) << "Reaching max depth when deleting " << dev_name
+ << ". There may be devices referencing itself. Check `dmctl list devices -v`.";
+
+ auto& dm = DeviceMapper::Instance();
+ auto table = GetTableInfoIfExists(dev_name);
+ if (table.empty()) {
+ PCHECK(dm.DeleteDeviceIfExists(dev_name)) << dev_name;
+ return;
+ }
+
+ if (!known_allow_delete) {
+ for (const auto& target : table) {
+ auto base_device_string = GetLinearBaseDeviceString(target);
+ if (base_device_string.empty()) continue;
+ if (ShouldDeleteLoopDevice(
+ StringPrintf("/sys/dev/block/%s", base_device_string.data()))) {
+ known_allow_delete = true;
+ break;
+ }
+ }
+ }
+ if (!known_allow_delete) {
+ return;
+ }
+
+ std::string dev_string;
+ PCHECK(dm.GetDeviceString(dev_name, &dev_string));
+
+ std::vector<DeviceMapper::DmBlockDevice> devices;
+ PCHECK(dm.GetAvailableDevices(&devices));
+ for (const auto& child_dev : devices) {
+ auto child_bases = GetAllBaseDeviceStrings(child_dev.name());
+ if (child_bases.find(dev_string) != child_bases.end()) {
+ CheckDeleteDeviceMapperTree(child_dev.name(), true /* known_allow_delete */, depth - 1);
+ }
+ }
+
+ PCHECK(dm.DeleteDeviceIfExists(dev_name)) << dev_name;
+}
+
+// Attempt to clean up residues from previous runs.
+void CheckCleanupDeviceMapperDevices() {
+ auto& dm = DeviceMapper::Instance();
+ std::vector<DeviceMapper::DmBlockDevice> devices;
+ PCHECK(dm.GetAvailableDevices(&devices));
+
+ for (const auto& dev : devices) {
+ CheckDeleteDeviceMapperTree(dev.name());
+ }
+}
+
+void CheckUmount(const std::string& path) {
+ PCHECK(TEMP_FAILURE_RETRY(umount(path.data()) == 0) || errno == ENOENT || errno == EINVAL)
+ << path;
+}
+
+void CheckDetachLoopDevices(const std::set<std::string>& exclude_names = {}) {
+ // ~SnapshotFuzzEnv automatically does the following.
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(BLOCK_SYSFS), closedir);
+ PCHECK(dir != nullptr) << BLOCK_SYSFS;
+ LoopControl loop_control;
+ dirent* dp;
+ while ((dp = readdir(dir.get())) != nullptr) {
+ if (exclude_names.find(dp->d_name) != exclude_names.end()) {
+ continue;
+ }
+ if (!ShouldDeleteLoopDevice(StringPrintf("%s/%s", BLOCK_SYSFS, dp->d_name).data())) {
+ continue;
+ }
+ PCHECK(loop_control.Detach(StringPrintf("/dev/block/%s", dp->d_name).data()));
+ }
+}
+
+void CheckUmountAll() {
+ CheckUmount(std::string(MNT_DIR) + "/snapshot_fuzz_data");
+ CheckUmount(std::string(MNT_DIR) + "/" + FAKE_ROOT_NAME);
+}
+
+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;
+ PCHECK(rmdir(name_.c_str()) == 0 || errno == ENOENT) << name_;
+ }
+
+ private:
+ AutoDeleteDir(const std::string& path) : AutoDevice(path) {}
+};
+
+class AutoUnmount : public AutoDevice {
+ public:
+ ~AutoUnmount() {
+ if (!HasDevice()) return;
+ CheckUmount(name_);
+ }
+ AutoUnmount(const std::string& path) : AutoDevice(path) {}
+};
+
+class AutoUnmountTmpfs : public AutoUnmount {
+ 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));
+ }
+ private:
+ using AutoUnmount::AutoUnmount;
+};
+
+// 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_ = AutoUnmountTmpfs::New(ret->mount_path(), size);
+ if (!ret->auto_umount_mount_point_->HasDevice()) {
+ return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
+ }
+ // tmp_path() and persist_path does not need to be deleted upon destruction, hence it is
+ // not wrapped with AutoDeleteDir.
+ if (!Mkdir(ret->tmp_path())) {
+ return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
+ }
+ if (!Mkdir(ret->persist_path())) {
+ return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
+ }
+ return ret;
+ }
+ // Return the temporary scratch directory.
+ std::string tmp_path() const {
+ CHECK(HasDevice());
+ return mount_path() + "/tmp";
+ }
+ // Return the temporary scratch directory.
+ std::string persist_path() const {
+ CHECK(HasDevice());
+ return mount_path() + "/persist";
+ }
+ // Delete all contents in tmp_path() and start over. tmp_path() itself is re-created.
+ void CheckSoftReset() {
+ PCHECK(RmdirRecursive(tmp_path()));
+ PCHECK(Mkdir(tmp_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() {
+ CheckCleanupDeviceMapperDevices();
+ CheckDetachLoopDevices();
+ CheckUmountAll();
+
+ fake_root_ = AutoMemBasedDir::New(FAKE_ROOT_NAME, FAKE_ROOT_SIZE);
+ CHECK(fake_root_ != nullptr);
+ CHECK(fake_root_->HasDevice());
+ loop_control_ = std::make_unique<LoopControl>();
+
+ fake_data_mount_point_ = MNT_DIR + "/snapshot_fuzz_data"s;
+ auto_delete_data_mount_point_ = AutoDeleteDir::New(fake_data_mount_point_);
+ CHECK(auto_delete_data_mount_point_ != nullptr);
+ CHECK(auto_delete_data_mount_point_->HasDevice());
+
+ const auto& fake_persist_path = fake_root_->persist_path();
+ mapped_super_ = CheckMapImage(fake_persist_path + "/super.img", SUPER_IMAGE_SIZE,
+ loop_control_.get(), &fake_super_);
+ mapped_data_ = CheckMapImage(fake_persist_path + "/data.img", DATA_IMAGE_SIZE,
+ loop_control_.get(), &fake_data_block_device_);
+ mounted_data_ = CheckMountFormatData(fake_data_block_device_, fake_data_mount_point_);
+}
+
+SnapshotFuzzEnv::~SnapshotFuzzEnv() {
+ CheckCleanupDeviceMapperDevices();
+ mounted_data_ = nullptr;
+ auto_delete_data_mount_point_ = nullptr;
+ mapped_data_ = nullptr;
+ mapped_super_ = nullptr;
+ CheckDetachLoopDevices();
+ loop_control_ = nullptr;
+ fake_root_ = nullptr;
+ CheckUmountAll();
+}
+
+void CheckZeroFill(const std::string& file, size_t size) {
+ std::string zeros(size, '\0');
+ PCHECK(WriteStringToFile(zeros, file)) << "Cannot write zeros to " << file;
+}
+
+void SnapshotFuzzEnv::CheckSoftReset() {
+ fake_root_->CheckSoftReset();
+ CheckZeroFill(super(), SUPER_IMAGE_SIZE);
+ CheckCleanupDeviceMapperDevices();
+ CheckDetachLoopDevices({Basename(fake_super_), Basename(fake_data_block_device_)});
+}
+
+std::unique_ptr<IImageManager> SnapshotFuzzEnv::CheckCreateFakeImageManager(
+ const std::string& metadata_dir, const std::string& data_dir) {
+ PCHECK(Mkdir(metadata_dir));
+ PCHECK(Mkdir(data_dir));
+ return SnapshotFuzzImageManager::Open(metadata_dir, data_dir);
+}
+
+// Helper to create a loop device for a file.
+static void CheckCreateLoopDevice(LoopControl* control, const std::string& file,
+ const std::chrono::milliseconds& timeout_ms, std::string* path) {
+ static constexpr int kOpenFlags = O_RDWR | O_NOFOLLOW | O_CLOEXEC;
+ android::base::unique_fd file_fd(open(file.c_str(), kOpenFlags));
+ PCHECK(file_fd >= 0) << "Could not open file: " << file;
+ CHECK(control->Attach(file_fd, timeout_ms, path))
+ << "Could not create loop device for: " << file;
+}
+
+class AutoDetachLoopDevice : public AutoDevice {
+ public:
+ AutoDetachLoopDevice(LoopControl* control, const std::string& device)
+ : AutoDevice(device), control_(control) {}
+ ~AutoDetachLoopDevice() { PCHECK(control_->Detach(name_)) << name_; }
+
+ private:
+ LoopControl* control_;
+};
+
+std::unique_ptr<AutoDevice> SnapshotFuzzEnv::CheckMapImage(const std::string& img_path,
+ uint64_t size, LoopControl* control,
+ std::string* mapped_path) {
+ CheckZeroFill(img_path, size);
+ CheckCreateLoopDevice(control, img_path, 1s, mapped_path);
+
+ return std::make_unique<AutoDetachLoopDevice>(control, *mapped_path);
+}
+
+SnapshotTestModule SnapshotFuzzEnv::CheckCreateSnapshotManager(const SnapshotFuzzData& data) {
+ SnapshotTestModule ret;
+ auto partition_opener = std::make_unique<TestPartitionOpener>(super());
+ ret.opener = partition_opener.get();
+ CheckWriteSuperMetadata(data, *partition_opener);
+ auto metadata_dir = fake_root_->tmp_path() + "/snapshot_metadata";
+ PCHECK(Mkdir(metadata_dir));
+ if (data.has_metadata_snapshots_dir()) {
+ PCHECK(Mkdir(metadata_dir + "/snapshots"));
+ }
+
+ ret.device_info = new SnapshotFuzzDeviceInfo(data.device_info_data(),
+ std::move(partition_opener), metadata_dir);
+ auto snapshot = SnapshotManager::New(ret.device_info /* takes ownership */);
+ snapshot->images_ =
+ CheckCreateFakeImageManager(fake_root_->tmp_path() + "/images_manager_metadata",
+ fake_data_mount_point_ + "/image_manager_data");
+ snapshot->has_local_image_manager_ = data.manager_data().is_local_image_manager();
+ ret.snapshot = std::move(snapshot);
+
+ return ret;
+}
+
+const std::string& SnapshotFuzzEnv::super() const {
+ return fake_super_;
+}
+
+void SnapshotFuzzEnv::CheckWriteSuperMetadata(const SnapshotFuzzData& data,
+ const IPartitionOpener& opener) {
+ if (!data.is_super_metadata_valid()) {
+ // Leave it zero.
+ return;
+ }
+
+ BlockDeviceInfo super_device("super", SUPER_IMAGE_SIZE, 0, 0, 4096);
+ std::vector<BlockDeviceInfo> devices = {super_device};
+ auto builder = MetadataBuilder::New(devices, "super", 65536, 2);
+ CHECK(builder != nullptr);
+
+ // Attempt to create a super partition metadata using proto. All errors are ignored.
+ for (const auto& group_proto : data.super_data().dynamic_partition_metadata().groups()) {
+ (void)builder->AddGroup(group_proto.name(), group_proto.size());
+ for (const auto& partition_name : group_proto.partition_names()) {
+ (void)builder->AddPartition(partition_name, group_proto.name(),
+ LP_PARTITION_ATTR_READONLY);
+ }
+ }
+
+ for (const auto& partition_proto : data.super_data().partitions()) {
+ auto p = builder->FindPartition(partition_proto.partition_name());
+ if (p == nullptr) continue;
+ (void)builder->ResizePartition(p, partition_proto.new_partition_info().size());
+ }
+
+ auto metadata = builder->Export();
+ // metadata may be nullptr if it is not valid (e.g. partition name too long).
+ // In this case, just use empty super partition data.
+ if (metadata == nullptr) {
+ builder = MetadataBuilder::New(devices, "super", 65536, 2);
+ CHECK(builder != nullptr);
+ metadata = builder->Export();
+ CHECK(metadata != nullptr);
+ }
+ CHECK(FlashPartitionTable(opener, super(), *metadata.get()));
+}
+
+std::unique_ptr<AutoDevice> SnapshotFuzzEnv::CheckMountFormatData(const std::string& blk_device,
+ const std::string& mount_point) {
+ FstabEntry entry{
+ .blk_device = blk_device,
+ .length = static_cast<off64_t>(DATA_IMAGE_SIZE),
+ .fs_type = "ext4",
+ .mount_point = mount_point,
+ };
+ CHECK(0 == fs_mgr_do_format(entry, false /* crypt_footer */));
+ CHECK(0 == fs_mgr_do_mount_one(entry));
+ return std::make_unique<AutoUnmount>(mount_point);
+}
+
+SnapshotFuzzImageManager::~SnapshotFuzzImageManager() {
+ // Remove relevant gsid.mapped_images.* props.
+ for (const auto& name : mapped_) {
+ CHECK(UnmapImageIfExists(name)) << "Cannot unmap " << name;
+ }
+}
+
+bool SnapshotFuzzImageManager::MapImageDevice(const std::string& name,
+ const std::chrono::milliseconds& timeout_ms,
+ std::string* path) {
+ if (impl_->MapImageDevice(name, timeout_ms, path)) {
+ mapped_.insert(name);
+ return true;
+ }
+ return false;
+}
+
+} // 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..fa327b8
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
@@ -0,0 +1,203 @@
+// 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 <memory>
+#include <set>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android/snapshot/snapshot_fuzz.pb.h>
+#include <libdm/loop_control.h>
+#include <libfiemap/image_manager.h>
+#include <liblp/liblp.h>
+#include <libsnapshot/auto_device.h>
+#include <libsnapshot/test_helpers.h>
+
+// libsnapshot-specific code for fuzzing. Defines fake classes that are depended
+// by SnapshotManager.
+
+#include "android/snapshot/snapshot_fuzz.pb.h"
+
+namespace android::snapshot {
+
+class AutoMemBasedDir;
+class SnapshotFuzzDeviceInfo;
+
+class DummyAutoDevice : public AutoDevice {
+ public:
+ DummyAutoDevice(bool mounted) : AutoDevice(mounted ? "dummy" : "") {}
+};
+
+struct SnapshotTestModule {
+ std::unique_ptr<ISnapshotManager> snapshot;
+ SnapshotFuzzDeviceInfo* device_info = nullptr;
+ TestPartitionOpener* opener = nullptr;
+};
+
+// 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();
+
+ // Soft reset part of the environment before running the next test.
+ // Abort if fails.
+ void CheckSoftReset();
+
+ // Create a snapshot manager for this test run.
+ // Client is responsible for maintaining the lifetime of |data| over the life time of
+ // ISnapshotManager.
+ SnapshotTestModule CheckCreateSnapshotManager(const SnapshotFuzzData& data);
+
+ // Return path to super partition.
+ const std::string& super() const;
+
+ private:
+ std::unique_ptr<AutoMemBasedDir> fake_root_;
+ std::unique_ptr<android::dm::LoopControl> loop_control_;
+ std::string fake_data_mount_point_;
+ std::unique_ptr<AutoDevice> auto_delete_data_mount_point_;
+ std::unique_ptr<AutoDevice> mapped_super_;
+ std::string fake_super_;
+ std::unique_ptr<AutoDevice> mapped_data_;
+ std::string fake_data_block_device_;
+ std::unique_ptr<AutoDevice> mounted_data_;
+
+ static std::unique_ptr<android::fiemap::IImageManager> CheckCreateFakeImageManager(
+ const std::string& metadata_dir, const std::string& data_dir);
+ static std::unique_ptr<AutoDevice> CheckMapImage(const std::string& fake_persist_path,
+ uint64_t size,
+ android::dm::LoopControl* control,
+ std::string* mapped_path);
+ static std::unique_ptr<AutoDevice> CheckMountFormatData(const std::string& blk_device,
+ const std::string& mount_point);
+
+ void CheckWriteSuperMetadata(const SnapshotFuzzData& proto,
+ const android::fs_mgr::IPartitionOpener& opener);
+};
+
+class SnapshotFuzzDeviceInfo : public ISnapshotManager::IDeviceInfo {
+ public:
+ // Client is responsible for maintaining the lifetime of |data|.
+ SnapshotFuzzDeviceInfo(const FuzzDeviceInfoData& 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 CurrentSlotIsA() ? "_a" : "_b"; }
+ std::string GetOtherSlotSuffix() const override { return CurrentSlotIsA() ? "_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(); }
+
+ void SwitchSlot() { switched_slot_ = !switched_slot_; }
+
+ private:
+ const FuzzDeviceInfoData* data_;
+ std::unique_ptr<TestPartitionOpener> partition_opener_;
+ std::string metadata_dir_;
+ bool switched_slot_ = false;
+
+ bool CurrentSlotIsA() const { return data_->slot_suffix_is_a() != switched_slot_; }
+};
+
+// A spy class on ImageManager implementation. Upon destruction, unmaps all images
+// map through this object.
+class SnapshotFuzzImageManager : public android::fiemap::IImageManager {
+ public:
+ static std::unique_ptr<SnapshotFuzzImageManager> Open(const std::string& metadata_dir,
+ const std::string& data_dir) {
+ auto impl = android::fiemap::ImageManager::Open(metadata_dir, data_dir);
+ if (impl == nullptr) return nullptr;
+ return std::unique_ptr<SnapshotFuzzImageManager>(
+ new SnapshotFuzzImageManager(std::move(impl)));
+ }
+
+ ~SnapshotFuzzImageManager();
+
+ // Spied APIs.
+ bool MapImageDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms,
+ std::string* path) override;
+
+ // Other functions call through.
+ android::fiemap::FiemapStatus CreateBackingImage(
+ const std::string& name, uint64_t size, int flags,
+ std::function<bool(uint64_t, uint64_t)>&& on_progress) override {
+ return impl_->CreateBackingImage(name, size, flags, std::move(on_progress));
+ }
+ bool DeleteBackingImage(const std::string& name) override {
+ return impl_->DeleteBackingImage(name);
+ }
+ bool UnmapImageDevice(const std::string& name) override {
+ return impl_->UnmapImageDevice(name);
+ }
+ bool BackingImageExists(const std::string& name) override {
+ return impl_->BackingImageExists(name);
+ }
+ bool IsImageMapped(const std::string& name) override { return impl_->IsImageMapped(name); }
+ bool MapImageWithDeviceMapper(const IPartitionOpener& opener, const std::string& name,
+ std::string* dev) override {
+ return impl_->MapImageWithDeviceMapper(opener, name, dev);
+ }
+ bool GetMappedImageDevice(const std::string& name, std::string* device) override {
+ return impl_->GetMappedImageDevice(name, device);
+ }
+ bool MapAllImages(const std::function<bool(std::set<std::string>)>& init) override {
+ return impl_->MapAllImages(init);
+ }
+ bool DisableImage(const std::string& name) override { return impl_->DisableImage(name); }
+ bool RemoveDisabledImages() override { return impl_->RemoveDisabledImages(); }
+ std::vector<std::string> GetAllBackingImages() override { return impl_->GetAllBackingImages(); }
+ android::fiemap::FiemapStatus ZeroFillNewImage(const std::string& name,
+ uint64_t bytes) override {
+ return impl_->ZeroFillNewImage(name, bytes);
+ }
+ bool RemoveAllImages() override { return impl_->RemoveAllImages(); }
+ bool UnmapImageIfExists(const std::string& name) override {
+ return impl_->UnmapImageIfExists(name);
+ }
+
+ private:
+ std::unique_ptr<android::fiemap::IImageManager> impl_;
+ std::set<std::string> mapped_;
+
+ SnapshotFuzzImageManager(std::unique_ptr<android::fiemap::IImageManager>&& impl)
+ : impl_(std::move(impl)) {}
+};
+
+} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/snapshot_stats.cpp b/fs_mgr/libsnapshot/snapshot_stats.cpp
index 635b47d..3723730 100644
--- a/fs_mgr/libsnapshot/snapshot_stats.cpp
+++ b/fs_mgr/libsnapshot/snapshot_stats.cpp
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "snapshot_stats.h"
+#include <libsnapshot/snapshot_stats.h>
#include <sstream>
@@ -23,22 +23,17 @@
namespace android {
namespace snapshot {
-SnapshotMergeStats::SnapshotMergeStats(SnapshotManager& parent) : parent_(parent) {
- init_time_ = std::chrono::steady_clock::now();
+SnapshotMergeStats* SnapshotMergeStats::GetInstance(SnapshotManager& parent) {
+ static SnapshotMergeStats g_instance(parent.GetMergeStateFilePath());
+ CHECK(g_instance.path_ == parent.GetMergeStateFilePath());
+ return &g_instance;
}
-SnapshotMergeStats::~SnapshotMergeStats() {
- std::string error;
- auto file_path = parent_.GetMergeStateFilePath();
- if (!android::base::RemoveFileIfExists(file_path, &error)) {
- LOG(ERROR) << "Failed to remove merge statistics file " << file_path << ": " << error;
- return;
- }
-}
+SnapshotMergeStats::SnapshotMergeStats(const std::string& path) : path_(path), running_(false) {}
bool SnapshotMergeStats::ReadState() {
std::string contents;
- if (!android::base::ReadFileToString(parent_.GetMergeStateFilePath(), &contents)) {
+ if (!android::base::ReadFileToString(path_, &contents)) {
PLOG(INFO) << "Read merge statistics file failed";
return false;
}
@@ -55,34 +50,82 @@
LOG(ERROR) << "Unable to serialize SnapshotMergeStats.";
return false;
}
- auto file_path = parent_.GetMergeStateFilePath();
- if (!WriteStringToFileAtomic(contents, file_path)) {
+ if (!WriteStringToFileAtomic(contents, path_)) {
PLOG(ERROR) << "Could not write to merge statistics file";
return false;
}
return true;
}
-void SnapshotMergeStats::Start() {
- report_.set_resume_count(0);
- report_.set_state(UpdateState::None);
- WriteState();
+bool SnapshotMergeStats::DeleteState() {
+ std::string error;
+ if (!android::base::RemoveFileIfExists(path_, &error)) {
+ LOG(ERROR) << "Failed to remove merge statistics file " << path_ << ": " << error;
+ return false;
+ }
+ return true;
}
-void SnapshotMergeStats::Resume() {
- if (!ReadState()) {
- return;
+bool SnapshotMergeStats::Start() {
+ if (running_) {
+ LOG(ERROR) << "SnapshotMergeStats running_ == " << running_;
+ return false;
}
- report_.set_resume_count(report_.resume_count() + 1);
- WriteState();
+ running_ = true;
+
+ start_time_ = std::chrono::steady_clock::now();
+ if (ReadState()) {
+ report_.set_resume_count(report_.resume_count() + 1);
+ } else {
+ report_.set_resume_count(0);
+ report_.set_state(UpdateState::None);
+ }
+
+ return WriteState();
}
void SnapshotMergeStats::set_state(android::snapshot::UpdateState state) {
report_.set_state(state);
}
-SnapshotMergeReport SnapshotMergeStats::GetReport() {
- return report_;
+void SnapshotMergeStats::set_cow_file_size(uint64_t cow_file_size) {
+ report_.set_cow_file_size(cow_file_size);
+ WriteState();
+}
+
+uint64_t SnapshotMergeStats::cow_file_size() {
+ return report_.cow_file_size();
+}
+
+class SnapshotMergeStatsResultImpl : public SnapshotMergeStats::Result {
+ public:
+ SnapshotMergeStatsResultImpl(const SnapshotMergeReport& report,
+ std::chrono::steady_clock::duration merge_time)
+ : report_(report), merge_time_(merge_time) {}
+ const SnapshotMergeReport& report() const override { return report_; }
+ std::chrono::steady_clock::duration merge_time() const override { return merge_time_; }
+
+ private:
+ SnapshotMergeReport report_;
+ std::chrono::steady_clock::duration merge_time_;
+};
+
+std::unique_ptr<SnapshotMergeStats::Result> SnapshotMergeStats::Finish() {
+ if (!running_) {
+ LOG(ERROR) << "SnapshotMergeStats running_ == " << running_;
+ return nullptr;
+ }
+ running_ = false;
+
+ auto result = std::make_unique<SnapshotMergeStatsResultImpl>(
+ report_, std::chrono::steady_clock::now() - start_time_);
+
+ // We still want to report result if state is not deleted. Just leave
+ // it there and move on. A side effect is that it may be reported over and
+ // over again in the future, but there is nothing we can do.
+ (void)DeleteState();
+
+ return result;
}
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapshot_stats.h b/fs_mgr/libsnapshot/snapshot_stats.h
deleted file mode 100644
index 60109a4..0000000
--- a/fs_mgr/libsnapshot/snapshot_stats.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma once
-
-#include <chrono>
-
-#include <android/snapshot/snapshot.pb.h>
-#include <libsnapshot/snapshot.h>
-
-namespace android {
-namespace snapshot {
-
-class SnapshotMergeStats {
- public:
- SnapshotMergeStats(SnapshotManager& parent);
- ~SnapshotMergeStats();
- void Start();
- void Resume();
- void set_state(android::snapshot::UpdateState state);
- SnapshotMergeReport GetReport();
-
- private:
- bool ReadState();
- bool WriteState();
-
- const SnapshotManager& parent_;
- SnapshotMergeReport report_;
- std::chrono::time_point<std::chrono::steady_clock> init_time_;
- std::chrono::time_point<std::chrono::steady_clock> end_time_;
-};
-
-} // namespace snapshot
-} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_stub.cpp b/fs_mgr/libsnapshot/snapshot_stub.cpp
new file mode 100644
index 0000000..9b6f758
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshot_stub.cpp
@@ -0,0 +1,132 @@
+// 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 <libsnapshot/snapshot_stub.h>
+
+#include <android-base/logging.h>
+
+#include <libsnapshot/snapshot_stats.h>
+
+using android::fs_mgr::CreateLogicalPartitionParams;
+using chromeos_update_engine::DeltaArchiveManifest;
+
+namespace android::snapshot {
+
+std::unique_ptr<ISnapshotManager> SnapshotManagerStub::New() {
+ return std::make_unique<SnapshotManagerStub>();
+}
+
+bool SnapshotManagerStub::BeginUpdate() {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return false;
+}
+
+bool SnapshotManagerStub::CancelUpdate() {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return false;
+}
+
+bool SnapshotManagerStub::FinishedSnapshotWrites(bool) {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return false;
+}
+
+bool SnapshotManagerStub::InitiateMerge(uint64_t*) {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return false;
+}
+
+UpdateState SnapshotManagerStub::ProcessUpdateState(const std::function<bool()>&,
+ const std::function<bool()>&) {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return UpdateState::None;
+}
+
+UpdateState SnapshotManagerStub::GetUpdateState(double*) {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return UpdateState::None;
+}
+
+Return SnapshotManagerStub::CreateUpdateSnapshots(const DeltaArchiveManifest&) {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return Return::Error();
+}
+
+bool SnapshotManagerStub::MapUpdateSnapshot(const CreateLogicalPartitionParams&, std::string*) {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return false;
+}
+
+bool SnapshotManagerStub::UnmapUpdateSnapshot(const std::string&) {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return false;
+}
+
+bool SnapshotManagerStub::NeedSnapshotsInFirstStageMount() {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return false;
+}
+
+bool SnapshotManagerStub::CreateLogicalAndSnapshotPartitions(const std::string&,
+ const std::chrono::milliseconds&) {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return false;
+}
+
+bool SnapshotManagerStub::HandleImminentDataWipe(const std::function<void()>&) {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return false;
+}
+
+bool SnapshotManagerStub::FinishMergeInRecovery() {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return false;
+}
+
+CreateResult SnapshotManagerStub::RecoveryCreateSnapshotDevices() {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return CreateResult::ERROR;
+}
+
+CreateResult SnapshotManagerStub::RecoveryCreateSnapshotDevices(
+ const std::unique_ptr<AutoDevice>&) {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return CreateResult::ERROR;
+}
+
+bool SnapshotManagerStub::Dump(std::ostream&) {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return false;
+}
+
+std::unique_ptr<AutoDevice> SnapshotManagerStub::EnsureMetadataMounted() {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return nullptr;
+}
+
+class SnapshotMergeStatsStub : public ISnapshotMergeStats {
+ bool Start() override { return false; }
+ void set_state(android::snapshot::UpdateState) override {}
+ void set_cow_file_size(uint64_t) override {}
+ uint64_t cow_file_size() override { return 0; }
+ std::unique_ptr<Result> Finish() override { return nullptr; }
+};
+
+ISnapshotMergeStats* SnapshotManagerStub::GetSnapshotMergeStatsInstance() {
+ static SnapshotMergeStatsStub snapshot_merge_stats;
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return &snapshot_merge_stats;
+}
+
+} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 0ffaa71..2bd0135 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -41,6 +41,11 @@
#include <libsnapshot/test_helpers.h>
#include "utility.h"
+// Mock classes are not used. Header included to ensure mocked class definition aligns with the
+// class itself.
+#include <libsnapshot/mock_device_info.h>
+#include <libsnapshot/mock_snapshot.h>
+
namespace android {
namespace snapshot {
@@ -320,7 +325,7 @@
// Simulate a reboot into the new slot.
AssertionResult SimulateReboot() {
lock_ = nullptr;
- if (!sm->FinishedSnapshotWrites()) {
+ if (!sm->FinishedSnapshotWrites(false)) {
return AssertionFailure();
}
if (!dm_.DeleteDevice("test_partition_b")) {
@@ -424,7 +429,7 @@
}
TEST_F(SnapshotTest, NoMergeBeforeReboot) {
- ASSERT_TRUE(sm->FinishedSnapshotWrites());
+ ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
// Merge should fail, since the slot hasn't changed.
ASSERT_FALSE(sm->InitiateMerge());
@@ -440,7 +445,7 @@
}
TEST_F(SnapshotTest, FirstStageMountAfterRollback) {
- ASSERT_TRUE(sm->FinishedSnapshotWrites());
+ ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
// We didn't change the slot, so we shouldn't need snapshots.
TestDeviceInfo* info = new TestDeviceInfo(fake_super);
@@ -476,7 +481,7 @@
lock_ = nullptr;
// Done updating.
- ASSERT_TRUE(sm->FinishedSnapshotWrites());
+ ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
test_device->set_slot_suffix("_b");
ASSERT_TRUE(sm->InitiateMerge());
@@ -1007,7 +1012,7 @@
ASSERT_TRUE(IsPartitionUnchanged(name));
}
- ASSERT_TRUE(sm->FinishedSnapshotWrites());
+ ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
// Simulate shutting down the device.
ASSERT_TRUE(UnmapAll());
@@ -1027,7 +1032,8 @@
}
// Initiate the merge and wait for it to be completed.
- ASSERT_EQ(UpdateState::MergeCompleted, init->InitiateMergeAndWait());
+ ASSERT_TRUE(init->InitiateMerge());
+ ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
// Check that the target partitions have the same content after the merge.
for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
@@ -1138,7 +1144,7 @@
ASSERT_TRUE(IsPartitionUnchanged(name));
}
- ASSERT_TRUE(sm->FinishedSnapshotWrites());
+ ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
// Simulate shutting down the device.
ASSERT_TRUE(UnmapAll());
@@ -1170,7 +1176,7 @@
// Test that if an update is applied but not booted into, it can be canceled.
TEST_F(SnapshotUpdateTest, CancelAfterApply) {
ASSERT_TRUE(sm->BeginUpdate());
- ASSERT_TRUE(sm->FinishedSnapshotWrites());
+ ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
ASSERT_TRUE(sm->CancelUpdate());
}
@@ -1187,7 +1193,7 @@
ASSERT_TRUE(sm->BeginUpdate());
ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
ASSERT_TRUE(MapUpdateSnapshots());
- ASSERT_TRUE(sm->FinishedSnapshotWrites());
+ ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
// Simulate shutting down the device.
ASSERT_TRUE(UnmapAll());
@@ -1201,7 +1207,8 @@
// Initiate the merge and wait for it to be completed.
auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, "_b"));
- ASSERT_EQ(UpdateState::MergeCompleted, new_sm->InitiateMergeAndWait());
+ ASSERT_TRUE(new_sm->InitiateMerge());
+ ASSERT_EQ(UpdateState::MergeCompleted, new_sm->ProcessUpdateState());
// Execute the second update.
ASSERT_TRUE(new_sm->BeginUpdate());
@@ -1293,7 +1300,7 @@
ASSERT_TRUE(IsPartitionUnchanged(name));
}
- ASSERT_TRUE(sm->FinishedSnapshotWrites());
+ ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
}
TEST_F(SnapshotUpdateTest, MergeCannotRemoveCow) {
@@ -1322,7 +1329,7 @@
ASSERT_TRUE(sm->BeginUpdate());
ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
ASSERT_TRUE(MapUpdateSnapshots());
- ASSERT_TRUE(sm->FinishedSnapshotWrites());
+ ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
// Simulate shutting down the device.
ASSERT_TRUE(UnmapAll());
@@ -1341,7 +1348,8 @@
ASSERT_GE(fd, 0);
// COW cannot be removed due to open fd, so expect a soft failure.
- ASSERT_EQ(UpdateState::MergeNeedsReboot, init->InitiateMergeAndWait());
+ ASSERT_TRUE(init->InitiateMerge());
+ ASSERT_EQ(UpdateState::MergeNeedsReboot, init->ProcessUpdateState());
// Simulate shutting down the device.
fd.reset();
@@ -1354,7 +1362,7 @@
ASSERT_FALSE(sm->IsSnapshotDevice("sys_b", nullptr));
// Merge should be able to complete now.
- ASSERT_EQ(UpdateState::MergeCompleted, init->InitiateMergeAndWait());
+ ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
}
class MetadataMountedTest : public SnapshotUpdateTest {
@@ -1425,7 +1433,7 @@
ASSERT_TRUE(sm->BeginUpdate());
ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
ASSERT_TRUE(MapUpdateSnapshots());
- ASSERT_TRUE(sm->FinishedSnapshotWrites());
+ ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
// Simulate shutting down the device.
ASSERT_TRUE(UnmapAll());
@@ -1451,13 +1459,59 @@
ASSERT_EQ(new_sm->GetUpdateState(), UpdateState::None);
}
+// Test that a merge does not clear the snapshot state in fastboot.
+TEST_F(SnapshotUpdateTest, MergeInFastboot) {
+ // Execute the first update.
+ ASSERT_TRUE(sm->BeginUpdate());
+ ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+ ASSERT_TRUE(MapUpdateSnapshots());
+ ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
+
+ // Simulate shutting down the device.
+ ASSERT_TRUE(UnmapAll());
+
+ // After reboot, init does first stage mount.
+ auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
+ ASSERT_NE(init, nullptr);
+ ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
+ ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
+ init = nullptr;
+
+ // Initiate the merge and then immediately stop it to simulate a reboot.
+ auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, "_b"));
+ ASSERT_TRUE(new_sm->InitiateMerge());
+ ASSERT_TRUE(UnmapAll());
+
+ // Simulate a reboot into recovery.
+ auto test_device = std::make_unique<TestDeviceInfo>(fake_super, "_b");
+ test_device->set_recovery(true);
+ new_sm = SnapshotManager::NewForFirstStageMount(test_device.release());
+
+ ASSERT_TRUE(new_sm->FinishMergeInRecovery());
+
+ auto mount = new_sm->EnsureMetadataMounted();
+ ASSERT_TRUE(mount && mount->HasDevice());
+ ASSERT_EQ(new_sm->ProcessUpdateState(), UpdateState::MergeCompleted);
+
+ // Finish the merge in a normal boot.
+ test_device = std::make_unique<TestDeviceInfo>(fake_super, "_b");
+ init = SnapshotManager::NewForFirstStageMount(test_device.release());
+ ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
+ init = nullptr;
+
+ test_device = std::make_unique<TestDeviceInfo>(fake_super, "_b");
+ new_sm = SnapshotManager::NewForFirstStageMount(test_device.release());
+ ASSERT_EQ(new_sm->ProcessUpdateState(), UpdateState::MergeCompleted);
+ ASSERT_EQ(new_sm->ProcessUpdateState(), UpdateState::None);
+}
+
// Test that after an OTA, before a merge, we can wipe data in recovery.
TEST_F(SnapshotUpdateTest, DataWipeRollbackInRecovery) {
// Execute the first update.
ASSERT_TRUE(sm->BeginUpdate());
ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
ASSERT_TRUE(MapUpdateSnapshots());
- ASSERT_TRUE(sm->FinishedSnapshotWrites());
+ ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
// Simulate shutting down the device.
ASSERT_TRUE(UnmapAll());
@@ -1482,7 +1536,7 @@
ASSERT_TRUE(sm->BeginUpdate());
ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
ASSERT_TRUE(MapUpdateSnapshots());
- ASSERT_TRUE(sm->FinishedSnapshotWrites());
+ ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
// Simulate shutting down the device.
ASSERT_TRUE(UnmapAll());
@@ -1495,7 +1549,46 @@
ASSERT_TRUE(new_sm->HandleImminentDataWipe());
EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::None);
EXPECT_FALSE(test_device->IsSlotUnbootable(0));
- EXPECT_FALSE(test_device->IsSlotUnbootable(0));
+ EXPECT_FALSE(test_device->IsSlotUnbootable(1));
+}
+
+// Test update package that requests data wipe.
+TEST_F(SnapshotUpdateTest, DataWipeRequiredInPackage) {
+ AddOperationForPartitions();
+ // Execute the update.
+ ASSERT_TRUE(sm->BeginUpdate());
+ ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+
+ // Write some data to target partitions.
+ for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
+ ASSERT_TRUE(WriteSnapshotAndHash(name)) << name;
+ }
+
+ ASSERT_TRUE(sm->FinishedSnapshotWrites(true /* wipe */));
+
+ // Simulate shutting down the device.
+ ASSERT_TRUE(UnmapAll());
+
+ // Simulate a reboot into recovery.
+ auto test_device = new TestDeviceInfo(fake_super, "_b");
+ test_device->set_recovery(true);
+ auto new_sm = SnapshotManager::NewForFirstStageMount(test_device);
+
+ ASSERT_TRUE(new_sm->HandleImminentDataWipe());
+ // Manually mount metadata so that we can call GetUpdateState() below.
+ MountMetadata();
+ EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::None);
+ ASSERT_FALSE(test_device->IsSlotUnbootable(1));
+ ASSERT_FALSE(test_device->IsSlotUnbootable(0));
+
+ // Now reboot into new slot.
+ test_device = new TestDeviceInfo(fake_super, "_b");
+ auto init = SnapshotManager::NewForFirstStageMount(test_device);
+ ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
+ // Verify that we are on the downgraded build.
+ for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
+ ASSERT_TRUE(IsPartitionUnchanged(name)) << name;
+ }
}
TEST_F(SnapshotUpdateTest, Hashtree) {
@@ -1530,7 +1623,7 @@
ASSERT_TRUE(WriteSnapshotAndHash("sys_b", partition_size));
// Finish update.
- ASSERT_TRUE(sm->FinishedSnapshotWrites());
+ ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
// Simulate shutting down the device.
ASSERT_TRUE(UnmapAll());
@@ -1566,49 +1659,10 @@
ASSERT_EQ(1u, table.size());
EXPECT_TRUE(table[0].IsOverflowSnapshot());
- ASSERT_FALSE(sm->FinishedSnapshotWrites())
+ ASSERT_FALSE(sm->FinishedSnapshotWrites(false))
<< "FinishedSnapshotWrites should detect overflow of CoW device.";
}
-TEST_F(SnapshotUpdateTest, WaitForMerge) {
- AddOperationForPartitions();
-
- // Execute the update.
- ASSERT_TRUE(sm->BeginUpdate());
- ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-
- // Write some data to target partitions.
- for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
- ASSERT_TRUE(WriteSnapshotAndHash(name));
- }
-
- ASSERT_TRUE(sm->FinishedSnapshotWrites());
-
- // Simulate shutting down the device.
- ASSERT_TRUE(UnmapAll());
-
- // After reboot, init does first stage mount.
- {
- auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
- ASSERT_NE(nullptr, init);
- ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
- }
-
- auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, "_b"));
- ASSERT_NE(nullptr, new_sm);
-
- auto waiter = std::async(std::launch::async, [&new_sm] { return new_sm->WaitForMerge(); });
- ASSERT_EQ(std::future_status::timeout, waiter.wait_for(1s))
- << "WaitForMerge should block when not initiated";
-
- auto merger =
- std::async(std::launch::async, [&new_sm] { return new_sm->InitiateMergeAndWait(); });
- // Small images, so should be merged pretty quickly.
- ASSERT_EQ(std::future_status::ready, waiter.wait_for(3s)) << "WaitForMerge did not finish";
- ASSERT_TRUE(waiter.get());
- ASSERT_THAT(merger.get(), AnyOf(UpdateState::None, UpdateState::MergeCompleted));
-}
-
TEST_F(SnapshotUpdateTest, LowSpace) {
static constexpr auto kMaxFree = 10_MiB;
auto userdata = std::make_unique<LowSpaceUserdata>();
@@ -1659,7 +1713,7 @@
ASSERT_TRUE(sm->BeginUpdate());
ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
ASSERT_TRUE(MapUpdateSnapshots());
- ASSERT_TRUE(sm->FinishedSnapshotWrites());
+ ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
// Simulate shutting down the device.
ASSERT_TRUE(UnmapAll());
@@ -1730,7 +1784,8 @@
// There should be no snapshot to merge.
auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, flashed_slot_suffix));
- ASSERT_EQ(UpdateState::Cancelled, new_sm->InitiateMergeAndWait());
+ // update_enigne calls ProcessUpdateState first -- should see Cancelled.
+ ASSERT_EQ(UpdateState::Cancelled, new_sm->ProcessUpdateState());
// Next OTA calls CancelUpdate no matter what.
ASSERT_TRUE(new_sm->CancelUpdate());
@@ -1749,7 +1804,6 @@
protected:
void SetUp() override {
if (!is_virtual_ab_) GTEST_SKIP() << "Test for Virtual A/B devices only";
- GTEST_SKIP() << "WIP failure b/149738928";
SnapshotTest::SetUp();
userdata_ = std::make_unique<LowSpaceUserdata>();
diff --git a/fs_mgr/libsnapshot/snapshotctl.cpp b/fs_mgr/libsnapshot/snapshotctl.cpp
index e35ad4b..a44de84 100644
--- a/fs_mgr/libsnapshot/snapshotctl.cpp
+++ b/fs_mgr/libsnapshot/snapshotctl.cpp
@@ -24,10 +24,8 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
-#include <libsnapshot/snapshot.h>
-#include "utility.h"
-#include "utility.h"
+#include <libsnapshot/snapshot.h>
using namespace std::string_literals;
@@ -37,11 +35,8 @@
"Actions:\n"
" dump\n"
" Print snapshot states.\n"
- " merge [--logcat] [--log-to-file]\n"
- " Initialize merge and wait for it to be completed.\n"
- " If --logcat is specified, log to logcat.\n"
- " If --log-to-file is specified, log to /data/misc/snapshotctl_log/.\n"
- " If both specified, log to both. If none specified, log to stdout.\n";
+ " merge\n"
+ " Deprecated.\n";
return EX_USAGE;
}
@@ -53,88 +48,9 @@
return SnapshotManager::New()->Dump(std::cout);
}
-class FileLogger {
- public:
- FileLogger() {
- static constexpr const char* kLogFilePath = "/data/misc/snapshotctl_log/";
- std::stringstream ss;
- ss << kLogFilePath << "snapshotctl." << Now() << ".log";
- fd_.reset(TEMP_FAILURE_RETRY(
- open(ss.str().c_str(),
- O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW | O_SYNC, 0644)));
- if (fd_ == -1) {
- PLOG(ERROR) << "Cannot open persistent log " << ss.str();
- return;
- }
- // Explicitly chmod again because mode in open() may be masked by umask.
- if (fchmod(fd_.get(), 0644) == -1) {
- PLOG(ERROR) << "Cannot chmod 0644 persistent log " << ss.str();
- return;
- }
- }
- // Copy-contuctor needed to be converted to std::function.
- FileLogger(const FileLogger& other) { fd_.reset(dup(other.fd_)); }
- void operator()(android::base::LogId, android::base::LogSeverity, const char* /*tag*/,
- const char* /*file*/, unsigned int /*line*/, const char* message) {
- if (fd_ == -1) return;
- std::stringstream ss;
- ss << Now() << ":" << message << "\n";
- (void)android::base::WriteStringToFd(ss.str(), fd_);
- }
-
- private:
- android::base::unique_fd fd_;
-};
-
-class MergeCmdLogger {
- public:
- MergeCmdLogger(int argc, char** argv) {
- for (int i = 0; i < argc; ++i) {
- if (argv[i] == "--logcat"s) {
- loggers_.push_back(android::base::LogdLogger());
- }
- if (argv[i] == "--log-to-file"s) {
- loggers_.push_back(std::move(FileLogger()));
- }
- }
- if (loggers_.empty()) {
- loggers_.push_back(&android::base::StdioLogger);
- }
- }
- void operator()(android::base::LogId id, android::base::LogSeverity severity, const char* tag,
- const char* file, unsigned int line, const char* message) {
- for (auto&& logger : loggers_) {
- logger(id, severity, tag, file, line, message);
- }
- }
-
- private:
- std::vector<android::base::LogFunction> loggers_;
-};
-
-bool MergeCmdHandler(int argc, char** argv) {
- auto begin = std::chrono::steady_clock::now();
-
- // 'snapshotctl merge' is stripped away from arguments to
- // Logger.
- android::base::InitLogging(argv);
- android::base::SetLogger(MergeCmdLogger(argc - 2, argv + 2));
-
- auto state = SnapshotManager::New()->InitiateMergeAndWait();
-
- // We could wind up in the Unverified state if the device rolled back or
- // hasn't fully rebooted. Ignore this.
- if (state == UpdateState::None || state == UpdateState::Unverified) {
- return true;
- }
- if (state == UpdateState::MergeCompleted) {
- auto end = std::chrono::steady_clock::now();
- auto passed = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count();
- LOG(INFO) << "Snapshot merged in " << passed << " ms.";
- return true;
- }
-
- LOG(ERROR) << "Snapshot failed to merge with state \"" << state << "\".";
+bool MergeCmdHandler(int /*argc*/, char** argv) {
+ android::base::InitLogging(argv, &android::base::StderrLogger);
+ LOG(WARNING) << "Deprecated. Call update_engine_client --merge instead.";
return false;
}
diff --git a/fs_mgr/libsnapshot/snapshotctl.rc b/fs_mgr/libsnapshot/snapshotctl.rc
deleted file mode 100644
index 5dbe352..0000000
--- a/fs_mgr/libsnapshot/snapshotctl.rc
+++ /dev/null
@@ -1,2 +0,0 @@
-on property:sys.boot_completed=1
- exec_background - root root -- /system/bin/snapshotctl merge --logcat --log-to-file
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
index b036606..f82a602 100644
--- a/fs_mgr/libsnapshot/test_helpers.cpp
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -212,8 +212,8 @@
return AssertionFailure() << strerror(errno);
}
bsize_ = buf.f_bsize;
- free_space_ = buf.f_bsize * buf.f_bfree;
- available_space_ = buf.f_bsize * buf.f_bavail;
+ free_space_ = bsize_ * buf.f_bfree;
+ available_space_ = bsize_ * buf.f_bavail;
return AssertionSuccess();
}
diff --git a/fs_mgr/libsnapshot/update_engine/update_metadata.proto b/fs_mgr/libsnapshot/update_engine/update_metadata.proto
new file mode 100644
index 0000000..8a11eaa
--- /dev/null
+++ b/fs_mgr/libsnapshot/update_engine/update_metadata.proto
@@ -0,0 +1,80 @@
+//
+// 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.
+//
+
+// A subset of system/update_engine/update_metadata.proto. A separate file is
+// used here because:
+// - The original file is optimized for LITE_RUNTIME, but fuzzing needs
+// reflection.
+// - The definition here has less fields. libsnapshot only uses fields declared
+// here, and all fields declared here are fuzzed by libsnapshot_fuzzer. If
+// libsnapshot uses more fields in system/update_engine/update_metadata.proto
+// in the future, they must be added here too, otherwise it will fail to
+// compile.
+//
+// It is okay that this file is older than
+// system/update_engine/update_metadata.proto as long as the messages defined
+// here can also be parsed by protobuf defined there. However, it is not
+// okay to add fields here without adding them to
+// system/update_engine/update_metadata.proto. Doing so will cause a compiler
+// error when libsnapshot code starts to use these dangling fields.
+
+syntax = "proto2";
+
+package chromeos_update_engine;
+
+message Extent {
+ optional uint64 start_block = 1;
+ optional uint64 num_blocks = 2;
+}
+
+message PartitionInfo {
+ optional uint64 size = 1;
+}
+
+message InstallOperation {
+ enum Type {
+ SOURCE_COPY = 4;
+ // Not used by libsnapshot. Declared here so that the fuzzer has an
+ // alternative value to use for |type|.
+ ZERO = 6;
+ }
+ required Type type = 1;
+ repeated Extent src_extents = 4;
+ repeated Extent dst_extents = 6;
+}
+
+message PartitionUpdate {
+ required string partition_name = 1;
+ optional PartitionInfo new_partition_info = 7;
+ repeated InstallOperation operations = 8;
+ optional Extent hash_tree_extent = 11;
+ optional Extent fec_extent = 15;
+}
+
+message DynamicPartitionGroup {
+ required string name = 1;
+ optional uint64 size = 2;
+ repeated string partition_names = 3;
+}
+
+message DynamicPartitionMetadata {
+ repeated DynamicPartitionGroup groups = 1;
+}
+
+message DeltaArchiveManifest {
+ repeated PartitionUpdate partitions = 13;
+ optional DynamicPartitionMetadata dynamic_partition_metadata = 15;
+}
diff --git a/fs_mgr/libsnapshot/utility.cpp b/fs_mgr/libsnapshot/utility.cpp
index 3318b33..d32b61e 100644
--- a/fs_mgr/libsnapshot/utility.cpp
+++ b/fs_mgr/libsnapshot/utility.cpp
@@ -34,6 +34,7 @@
using android::fs_mgr::MetadataBuilder;
using android::fs_mgr::Partition;
using android::fs_mgr::ReadDefaultFstab;
+using google::protobuf::RepeatedPtrField;
namespace android {
namespace snapshot {
@@ -166,5 +167,20 @@
return os << std::put_time(&now, "%Y%m%d-%H%M%S");
}
+void AppendExtent(RepeatedPtrField<chromeos_update_engine::Extent>* extents, uint64_t start_block,
+ uint64_t num_blocks) {
+ if (extents->size() > 0) {
+ auto last_extent = extents->rbegin();
+ auto next_block = last_extent->start_block() + last_extent->num_blocks();
+ if (start_block == next_block) {
+ last_extent->set_num_blocks(last_extent->num_blocks() + num_blocks);
+ return;
+ }
+ }
+ auto* new_extent = extents->Add();
+ new_extent->set_start_block(start_block);
+ new_extent->set_num_blocks(num_blocks);
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/utility.h b/fs_mgr/libsnapshot/utility.h
index 90ad0fe..e69bdad 100644
--- a/fs_mgr/libsnapshot/utility.h
+++ b/fs_mgr/libsnapshot/utility.h
@@ -125,5 +125,9 @@
struct Now {};
std::ostream& operator<<(std::ostream& os, const Now&);
+// Append to |extents|. Merged into the last element if possible.
+void AppendExtent(google::protobuf::RepeatedPtrField<chromeos_update_engine::Extent>* extents,
+ uint64_t start_block, uint64_t num_blocks);
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/tests/Android.bp b/fs_mgr/tests/Android.bp
index 4f6ec5a..f68ab87 100644
--- a/fs_mgr/tests/Android.bp
+++ b/fs_mgr/tests/Android.bp
@@ -17,7 +17,7 @@
test_suites: [
"cts",
"device-tests",
- "vts",
+ "vts10",
],
compile_multilib: "both",
multilib: {
diff --git a/fs_mgr/tests/AndroidTest.xml b/fs_mgr/tests/AndroidTest.xml
index 0ff8995..de835b3 100644
--- a/fs_mgr/tests/AndroidTest.xml
+++ b/fs_mgr/tests/AndroidTest.xml
@@ -21,6 +21,9 @@
<option name="push" value="CtsFsMgrTestCases->/data/local/tmp/CtsFsMgrTestCases" />
<option name="append-bitness" value="true" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+ <option name="throw-on-error" value="false" />
+ </target_preparer>
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
<option name="module-name" value="CtsFsMgrTestCases" />
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index cf324fe..82c4262 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -732,6 +732,7 @@
grep -v \
-e "^\(overlay\|tmpfs\|none\|sysfs\|proc\|selinuxfs\|debugfs\|bpf\) " \
-e "^\(binfmt_misc\|cg2_bpf\|pstore\|tracefs\|adb\|mtp\|ptp\|devpts\) " \
+ -e " functionfs " \
-e "^\(/data/media\|/dev/block/loop[0-9]*\) " \
-e "^rootfs / rootfs rw," \
-e " /\(cache\|mnt/scratch\|mnt/vendor/persist\|persist\|metadata\) "
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index 69b3150..27c8aae 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -27,6 +27,7 @@
#include <android-base/file.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
+#include <fs_mgr.h>
#include <fstab/fstab.h>
#include <gtest/gtest.h>
@@ -1015,6 +1016,12 @@
}
Fstab fstab;
ASSERT_TRUE(ReadDefaultFstab(&fstab)) << "Failed to read default fstab";
- ASSERT_NE(nullptr, GetMountedEntryForUserdata(&fstab))
+ Fstab proc_mounts;
+ ASSERT_TRUE(ReadFstabFromFile("/proc/mounts", &proc_mounts)) << "Failed to read /proc/mounts";
+ auto mounted_entry = GetEntryForMountPoint(&proc_mounts, "/data");
+ ASSERT_NE(mounted_entry, nullptr) << "/data is not mounted";
+ std::string block_device;
+ ASSERT_TRUE(android::base::Realpath(mounted_entry->blk_device, &block_device));
+ ASSERT_NE(nullptr, fs_mgr_get_mounted_entry_for_userdata(&fstab, block_device))
<< "/data wasn't mounted from default fstab";
}
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index 8e9e074..599f500 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -233,6 +233,15 @@
return value;
}
+bool BatteryMonitor::isScopedPowerSupply(const char* name) {
+ constexpr char kScopeDevice[] = "Device";
+
+ String8 path;
+ path.appendFormat("%s/%s/scope", POWER_SUPPLY_SYSFS_PATH, name);
+ std::string scope;
+ return (readFromFile(path, &scope) > 0 && scope == kScopeDevice);
+}
+
void BatteryMonitor::updateValues(void) {
initHealthInfo(mHealthInfo.get());
@@ -249,7 +258,7 @@
props.batteryVoltage = getIntField(mHealthdConfig->batteryVoltagePath) / 1000;
if (!mHealthdConfig->batteryCurrentNowPath.isEmpty())
- props.batteryCurrent = getIntField(mHealthdConfig->batteryCurrentNowPath) / 1000;
+ props.batteryCurrent = getIntField(mHealthdConfig->batteryCurrentNowPath);
if (!mHealthdConfig->batteryFullChargePath.isEmpty())
props.batteryFullCharge = getIntField(mHealthdConfig->batteryFullChargePath);
@@ -547,6 +556,11 @@
break;
case ANDROID_POWER_SUPPLY_TYPE_BATTERY:
+ // Some devices expose the battery status of sub-component like
+ // stylus. Such a device-scoped battery info needs to be skipped
+ // in BatteryMonitor, which is intended to report the status of
+ // the battery supplying the power to the whole system.
+ if (isScopedPowerSupply(name)) continue;
mBatteryDevicePresent = true;
if (mHealthdConfig->batteryStatusPath.isEmpty()) {
diff --git a/healthd/include/healthd/BatteryMonitor.h b/healthd/include/healthd/BatteryMonitor.h
index d41a374..fadb5a5 100644
--- a/healthd/include/healthd/BatteryMonitor.h
+++ b/healthd/include/healthd/BatteryMonitor.h
@@ -78,6 +78,7 @@
PowerSupplyType readPowerSupplyType(const String8& path);
bool getBooleanField(const String8& path);
int getIntField(const String8& path);
+ bool isScopedPowerSupply(const char* name);
};
}; // namespace android
diff --git a/init/Android.bp b/init/Android.bp
index 52628f3..edf9099 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -28,7 +28,6 @@
"rlimit_parser.cpp",
"service.cpp",
"service_list.cpp",
- "service_lock.cpp",
"service_parser.cpp",
"service_utils.cpp",
"subcontext.cpp",
@@ -37,10 +36,12 @@
"util.cpp",
]
init_device_sources = [
+ "block_dev_initializer.cpp",
"bootchart.cpp",
"builtins.cpp",
"devices.cpp",
"firmware_handler.cpp",
+ "first_stage_console.cpp",
"first_stage_init.cpp",
"first_stage_mount.cpp",
"fscrypt_init_extensions.cpp",
@@ -130,6 +131,7 @@
"libpropertyinfoparser",
"libsnapshot_init",
"lib_apex_manifest_proto_lite",
+ "update_metadata-protos",
],
shared_libs: [
"libbacktrace",
@@ -241,6 +243,7 @@
"firmware_handler_test.cpp",
"init_test.cpp",
"keychords_test.cpp",
+ "oneshot_on_test.cpp",
"persistent_properties_test.cpp",
"property_service_test.cpp",
"property_type_test.cpp",
@@ -257,7 +260,7 @@
test_suites: [
"cts",
"device-tests",
- "vts",
+ "vts10",
],
}
diff --git a/init/Android.mk b/init/Android.mk
index 07b0f95..da94daf 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -48,7 +48,9 @@
include $(CLEAR_VARS)
LOCAL_CPPFLAGS := $(init_cflags)
LOCAL_SRC_FILES := \
+ block_dev_initializer.cpp \
devices.cpp \
+ first_stage_console.cpp \
first_stage_init.cpp \
first_stage_main.cpp \
first_stage_mount.cpp \
@@ -105,13 +107,13 @@
libgsi \
libcom.android.sysprop.apex \
liblzma \
- libdexfile_support_static \
- libunwindstack \
- libbacktrace \
+ libunwindstack_no_dex \
+ libbacktrace_no_dex \
libmodprobe \
libext2_uuid \
libprotobuf-cpp-lite \
libsnapshot_init \
+ update_metadata-protos \
LOCAL_SANITIZE := signed-integer-overflow
# First stage init is weird: it may start without stdout/stderr, and no /proc.
diff --git a/init/AndroidTest.xml b/init/AndroidTest.xml
index 920dc6c..17f509a 100644
--- a/init/AndroidTest.xml
+++ b/init/AndroidTest.xml
@@ -24,6 +24,9 @@
<option name="push" value="CtsInitTestCases->/data/local/tmp/CtsInitTestCases" />
<option name="append-bitness" value="true" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+ <option name="throw-on-error" value="false" />
+ </target_preparer>
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
<option name="module-name" value="CtsInitTestCases" />
diff --git a/init/README.md b/init/README.md
index 4f0a7ec..0dd1490 100644
--- a/init/README.md
+++ b/init/README.md
@@ -322,6 +322,10 @@
This is mutually exclusive with the console option, which additionally connects stdin to the
given console.
+`task_profiles <profile> [ <profile>\* ]`
+> Set task profiles for the process when it forks. This is designed to replace the use of
+ writepid option for moving a process into a cgroup.
+
`timeout_period <seconds>`
> Provide a timeout after which point the service will be killed. The oneshot keyword is respected
here, so oneshot services do not automatically restart, however all other services will.
@@ -356,6 +360,8 @@
cgroup/cpuset usage. If no files under /dev/cpuset/ are specified, but the
system property 'ro.cpuset.default' is set to a non-empty cpuset name (e.g.
'/foreground'), then the pid is written to file /dev/cpuset/_cpuset\_name_/tasks.
+ The use of this option for moving a process into a cgroup is obsolete. Please
+ use task_profiles option instead.
Triggers
@@ -541,13 +547,16 @@
* `ref`: use the systemwide DE key
* `per_boot_ref`: use the key freshly generated on each boot.
-`mount_all <fstab> [ <path> ]\* [--<option>]`
+`mount_all [ <fstab> ] [--<option>]`
> Calls fs\_mgr\_mount\_all on the given fs\_mgr-format fstab with optional
options "early" and "late".
With "--early" set, the init executable will skip mounting entries with
"latemount" flag and triggering fs encryption state event. With "--late" set,
init executable will only mount entries with "latemount" flag. By default,
no option is set, and mount\_all will process all entries in the given fstab.
+ If the fstab parameter is not specified, fstab.${ro.boot.fstab_suffix},
+ fstab.${ro.hardware} or fstab.${ro.hardware.platform} will be scanned for
+ under /odm/etc, /vendor/etc, or / at runtime, in that order.
`mount <type> <device> <dir> [ <flag>\* ] [<options>]`
> Attempt to mount the named device at the directory _dir_
@@ -638,7 +647,8 @@
`wait <path> [ <timeout> ]`
> Poll for the existence of the given file and return when found,
or the timeout has been reached. If timeout is not specified it
- currently defaults to five seconds.
+ currently defaults to five seconds. The timeout value can be
+ fractional seconds, specified in floating point notation.
`wait_for_prop <name> <value>`
> Wait for system property _name_ to be _value_. Properties are expanded
@@ -720,23 +730,35 @@
characteristics in a device agnostic manner.
Init responds to properties that begin with `ctl.`. These properties take the format of
-`ctl.<command>` and the _value_ of the system property is used as a parameter, for example:
-`SetProperty("ctl.start", "logd")` will run the `start` command on `logd`. Note that these
+`ctl.[<target>_]<command>` and the _value_ of the system property is used as a parameter. The
+_target_ is optional and specifies the service option that _value_ is meant to match with. There is
+only one option for _target_, `interface` which indicates that _value_ will refer to an interface
+that a service provides and not the service name itself.
+
+For example:
+
+`SetProperty("ctl.start", "logd")` will run the `start` command on `logd`.
+
+`SetProperty("ctl.interface_start", "aidl/aidl_lazy_test_1")` will run the `start` command on the
+service that exposes the `aidl aidl_lazy_test_1` interface.
+
+Note that these
properties are only settable; they will have no value when read.
-`ctl.start` \
-`ctl.restart` \
-`ctl.stop`
-> These are equivalent to using the `start`, `restart`, and `stop` commands on the service specified
+The _commands_ are listed below.
+
+`start` \
+`restart` \
+`stop` \
+These are equivalent to using the `start`, `restart`, and `stop` commands on the service specified
by the _value_ of the property.
-`ctl.interface_start` \
-`ctl.interface_restart` \
-`ctl.interface_stop`
-> These are equivalent to using the `interface_start`, `interface_restart`, and `interface_stop`
-commands on the interface specified by the _value_ of the property.
+`oneshot_one` and `oneshot_off` will turn on or off the _oneshot_
+flag for the service specified by the _value_ of the property. This is
+particularly intended for services that are conditionally lazy HALs. When
+they are lazy HALs, oneshot must be on, otherwise oneshot should be off.
-`ctl.sigstop_on` and `ctl.sigstop_off` will turn on or off the _sigstop_ feature for the service
+`sigstop_on` and `sigstop_off` will turn on or off the _sigstop_ feature for the service
specified by the _value_ of the property. See the _Debugging init_ section below for more details
about this feature.
diff --git a/init/block_dev_initializer.cpp b/init/block_dev_initializer.cpp
new file mode 100644
index 0000000..b423f86
--- /dev/null
+++ b/init/block_dev_initializer.cpp
@@ -0,0 +1,148 @@
+// 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 <chrono>
+#include <string_view>
+#include <vector>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <fs_mgr.h>
+
+#include "block_dev_initializer.h"
+
+namespace android {
+namespace init {
+
+using android::base::Timer;
+using namespace std::chrono_literals;
+
+BlockDevInitializer::BlockDevInitializer() : uevent_listener_(16 * 1024 * 1024) {
+ auto boot_devices = android::fs_mgr::GetBootDevices();
+ device_handler_ = std::make_unique<DeviceHandler>(
+ std::vector<Permissions>{}, std::vector<SysfsPermissions>{}, std::vector<Subsystem>{},
+ std::move(boot_devices), false);
+}
+
+bool BlockDevInitializer::InitDeviceMapper() {
+ const std::string dm_path = "/devices/virtual/misc/device-mapper";
+ bool found = false;
+ auto dm_callback = [this, &dm_path, &found](const Uevent& uevent) {
+ if (uevent.path == dm_path) {
+ device_handler_->HandleUevent(uevent);
+ found = true;
+ return ListenerAction::kStop;
+ }
+ return ListenerAction::kContinue;
+ };
+ uevent_listener_.RegenerateUeventsForPath("/sys" + dm_path, dm_callback);
+ if (!found) {
+ LOG(INFO) << "device-mapper device not found in /sys, waiting for its uevent";
+ Timer t;
+ uevent_listener_.Poll(dm_callback, 10s);
+ LOG(INFO) << "Wait for device-mapper returned after " << t;
+ }
+ if (!found) {
+ LOG(ERROR) << "device-mapper device not found after polling timeout";
+ return false;
+ }
+ return true;
+}
+
+ListenerAction BlockDevInitializer::HandleUevent(const Uevent& uevent,
+ std::set<std::string>* devices) {
+ // Ignore everything that is not a block device.
+ if (uevent.subsystem != "block") {
+ return ListenerAction::kContinue;
+ }
+
+ auto name = uevent.partition_name;
+ if (name.empty()) {
+ size_t base_idx = uevent.path.rfind('/');
+ if (base_idx == std::string::npos) {
+ return ListenerAction::kContinue;
+ }
+ name = uevent.path.substr(base_idx + 1);
+ }
+
+ auto iter = devices->find(name);
+ if (iter == devices->end()) {
+ return ListenerAction::kContinue;
+ }
+
+ LOG(VERBOSE) << __PRETTY_FUNCTION__ << ": found partition: " << name;
+
+ devices->erase(iter);
+ device_handler_->HandleUevent(uevent);
+ return devices->empty() ? ListenerAction::kStop : ListenerAction::kContinue;
+}
+
+bool BlockDevInitializer::InitDevices(std::set<std::string> devices) {
+ auto uevent_callback = [&, this](const Uevent& uevent) -> ListenerAction {
+ return HandleUevent(uevent, &devices);
+ };
+ uevent_listener_.RegenerateUevents(uevent_callback);
+
+ // UeventCallback() will remove found partitions from |devices|. So if it
+ // isn't empty here, it means some partitions are not found.
+ if (!devices.empty()) {
+ LOG(INFO) << __PRETTY_FUNCTION__
+ << ": partition(s) not found in /sys, waiting for their uevent(s): "
+ << android::base::Join(devices, ", ");
+ Timer t;
+ uevent_listener_.Poll(uevent_callback, 10s);
+ LOG(INFO) << "Wait for partitions returned after " << t;
+ }
+
+ if (!devices.empty()) {
+ LOG(ERROR) << __PRETTY_FUNCTION__ << ": partition(s) not found after polling timeout: "
+ << android::base::Join(devices, ", ");
+ return false;
+ }
+ return true;
+}
+
+// Creates "/dev/block/dm-XX" for dm nodes by running coldboot on /sys/block/dm-XX.
+bool BlockDevInitializer::InitDmDevice(const std::string& device) {
+ const std::string device_name(basename(device.c_str()));
+ const std::string syspath = "/sys/block/" + device_name;
+ bool found = false;
+
+ auto uevent_callback = [&device_name, &device, this, &found](const Uevent& uevent) {
+ if (uevent.device_name == device_name) {
+ LOG(VERBOSE) << "Creating device-mapper device : " << device;
+ device_handler_->HandleUevent(uevent);
+ found = true;
+ return ListenerAction::kStop;
+ }
+ return ListenerAction::kContinue;
+ };
+
+ uevent_listener_.RegenerateUeventsForPath(syspath, uevent_callback);
+ if (!found) {
+ LOG(INFO) << "dm device '" << device << "' not found in /sys, waiting for its uevent";
+ Timer t;
+ uevent_listener_.Poll(uevent_callback, 10s);
+ LOG(INFO) << "wait for dm device '" << device << "' returned after " << t;
+ }
+ if (!found) {
+ LOG(ERROR) << "dm device '" << device << "' not found after polling timeout";
+ return false;
+ }
+ return true;
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/block_dev_initializer.h b/init/block_dev_initializer.h
new file mode 100644
index 0000000..0d4c6e9
--- /dev/null
+++ b/init/block_dev_initializer.h
@@ -0,0 +1,41 @@
+// 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 <memory>
+#include <set>
+#include <string>
+
+#include "devices.h"
+#include "uevent_listener.h"
+
+namespace android {
+namespace init {
+
+class BlockDevInitializer final {
+ public:
+ BlockDevInitializer();
+
+ bool InitDeviceMapper();
+ bool InitDevices(std::set<std::string> devices);
+ bool InitDmDevice(const std::string& device);
+
+ private:
+ ListenerAction HandleUevent(const Uevent& uevent, std::set<std::string>* devices);
+
+ std::unique_ptr<DeviceHandler> device_handler_;
+ UeventListener uevent_listener_;
+};
+
+} // namespace init
+} // namespace android
diff --git a/init/builtins.cpp b/init/builtins.cpp
index dd5af72..e918e12 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -49,6 +49,7 @@
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/parsedouble.h>
#include <android-base/parseint.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
@@ -151,7 +152,6 @@
template <typename F>
static void ForEachServiceInClass(const std::string& classname, F function) {
- auto lock = std::lock_guard{service_lock};
for (const auto& service : ServiceList::GetInstance()) {
if (service->classnames().count(classname)) std::invoke(function, service);
}
@@ -163,7 +163,6 @@
return {};
// Starting a class does not start services which are explicitly disabled.
// They must be started individually.
- auto lock = std::lock_guard{service_lock};
for (const auto& service : ServiceList::GetInstance()) {
if (service->classnames().count(args[1])) {
if (auto result = service->StartIfNotDisabled(); !result.ok()) {
@@ -186,7 +185,6 @@
// stopped either.
return {};
}
- auto lock = std::lock_guard{service_lock};
for (const auto& service : ServiceList::GetInstance()) {
if (service->classnames().count(args[1])) {
if (auto result = service->StartIfPostData(); !result.ok()) {
@@ -237,7 +235,6 @@
}
static Result<void> do_enable(const BuiltinArguments& args) {
- auto lock = std::lock_guard{service_lock};
Service* svc = ServiceList::GetInstance().FindService(args[1]);
if (!svc) return Error() << "Could not find service";
@@ -249,7 +246,6 @@
}
static Result<void> do_exec(const BuiltinArguments& args) {
- auto lock = std::lock_guard{service_lock};
auto service = Service::MakeTemporaryOneshotService(args.args);
if (!service.ok()) {
return Error() << "Could not create exec service: " << service.error();
@@ -263,7 +259,6 @@
}
static Result<void> do_exec_background(const BuiltinArguments& args) {
- auto lock = std::lock_guard{service_lock};
auto service = Service::MakeTemporaryOneshotService(args.args);
if (!service.ok()) {
return Error() << "Could not create exec background service: " << service.error();
@@ -277,7 +272,6 @@
}
static Result<void> do_exec_start(const BuiltinArguments& args) {
- auto lock = std::lock_guard{service_lock};
Service* service = ServiceList::GetInstance().FindService(args[1]);
if (!service) {
return Error() << "Service not found";
@@ -347,7 +341,6 @@
}
static Result<void> do_interface_restart(const BuiltinArguments& args) {
- auto lock = std::lock_guard{service_lock};
Service* svc = ServiceList::GetInstance().FindInterface(args[1]);
if (!svc) return Error() << "interface " << args[1] << " not found";
svc->Restart();
@@ -355,7 +348,6 @@
}
static Result<void> do_interface_start(const BuiltinArguments& args) {
- auto lock = std::lock_guard{service_lock};
Service* svc = ServiceList::GetInstance().FindInterface(args[1]);
if (!svc) return Error() << "interface " << args[1] << " not found";
if (auto result = svc->Start(); !result.ok()) {
@@ -365,7 +357,6 @@
}
static Result<void> do_interface_stop(const BuiltinArguments& args) {
- auto lock = std::lock_guard{service_lock};
Service* svc = ServiceList::GetInstance().FindInterface(args[1]);
if (!svc) return Error() << "interface " << args[1] << " not found";
svc->Stop();
@@ -527,21 +518,21 @@
/* Imports .rc files from the specified paths. Default ones are applied if none is given.
*
- * start_index: index of the first path in the args list
+ * rc_paths: list of paths to rc files to import
*/
-static void import_late(const std::vector<std::string>& args, size_t start_index, size_t end_index) {
+static void import_late(const std::vector<std::string>& rc_paths) {
auto& action_manager = ActionManager::GetInstance();
auto& service_list = ServiceList::GetInstance();
Parser parser = CreateParser(action_manager, service_list);
- if (end_index <= start_index) {
+ if (rc_paths.empty()) {
// Fallbacks for partitions on which early mount isn't enabled.
for (const auto& path : late_import_paths) {
parser.ParseConfig(path);
}
late_import_paths.clear();
} else {
- for (size_t i = start_index; i < end_index; ++i) {
- parser.ParseConfig(args[i]);
+ for (const auto& rc_path : rc_paths) {
+ parser.ParseConfig(rc_path);
}
}
@@ -642,48 +633,44 @@
static int initial_mount_fstab_return_code = -1;
-/* mount_all <fstab> [ <path> ]* [--<options>]*
+/* <= Q: mount_all <fstab> [ <path> ]* [--<options>]*
+ * >= R: mount_all [ <fstab> ] [--<options>]*
*
* This function might request a reboot, in which case it will
* not return.
*/
static Result<void> do_mount_all(const BuiltinArguments& args) {
- std::size_t na = 0;
- bool import_rc = true;
- bool queue_event = true;
- int mount_mode = MOUNT_MODE_DEFAULT;
- const auto& fstab_file = args[1];
- std::size_t path_arg_end = args.size();
- const char* prop_post_fix = "default";
+ auto mount_all = ParseMountAll(args.args);
+ if (!mount_all.ok()) return mount_all.error();
- for (na = args.size() - 1; na > 1; --na) {
- if (args[na] == "--early") {
- path_arg_end = na;
- queue_event = false;
- mount_mode = MOUNT_MODE_EARLY;
- prop_post_fix = "early";
- } else if (args[na] == "--late") {
- path_arg_end = na;
- import_rc = false;
- mount_mode = MOUNT_MODE_LATE;
- prop_post_fix = "late";
- }
+ const char* prop_post_fix = "default";
+ bool queue_event = true;
+ if (mount_all->mode == MOUNT_MODE_EARLY) {
+ prop_post_fix = "early";
+ queue_event = false;
+ } else if (mount_all->mode == MOUNT_MODE_LATE) {
+ prop_post_fix = "late";
}
std::string prop_name = "ro.boottime.init.mount_all."s + prop_post_fix;
android::base::Timer t;
Fstab fstab;
- if (!ReadFstabFromFile(fstab_file, &fstab)) {
- return Error() << "Could not read fstab";
+ if (mount_all->fstab_path.empty()) {
+ if (!ReadDefaultFstab(&fstab)) {
+ return Error() << "Could not read default fstab";
+ }
+ } else {
+ if (!ReadFstabFromFile(mount_all->fstab_path, &fstab)) {
+ return Error() << "Could not read fstab";
+ }
}
- auto mount_fstab_return_code = fs_mgr_mount_all(&fstab, mount_mode);
+ auto mount_fstab_return_code = fs_mgr_mount_all(&fstab, mount_all->mode);
SetProperty(prop_name, std::to_string(t.duration().count()));
- if (import_rc && SelinuxGetVendorAndroidVersion() <= __ANDROID_API_Q__) {
- /* Paths of .rc files are specified at the 2nd argument and beyond */
- import_late(args.args, 2, path_arg_end);
+ if (mount_all->import_rc) {
+ import_late(mount_all->rc_paths);
}
if (queue_event) {
@@ -699,11 +686,20 @@
return {};
}
-/* umount_all <fstab> */
+/* umount_all [ <fstab> ] */
static Result<void> do_umount_all(const BuiltinArguments& args) {
+ auto umount_all = ParseUmountAll(args.args);
+ if (!umount_all.ok()) return umount_all.error();
+
Fstab fstab;
- if (!ReadFstabFromFile(args[1], &fstab)) {
- return Error() << "Could not read fstab";
+ if (umount_all->empty()) {
+ if (!ReadDefaultFstab(&fstab)) {
+ return Error() << "Could not read default fstab";
+ }
+ } else {
+ if (!ReadFstabFromFile(*umount_all, &fstab)) {
+ return Error() << "Could not read fstab";
+ }
}
if (auto result = fs_mgr_umount_all(&fstab); result != 0) {
@@ -750,7 +746,6 @@
}
static Result<void> do_start(const BuiltinArguments& args) {
- auto lock = std::lock_guard{service_lock};
Service* svc = ServiceList::GetInstance().FindService(args[1]);
if (!svc) return Error() << "service " << args[1] << " not found";
if (auto result = svc->Start(); !result.ok()) {
@@ -760,7 +755,6 @@
}
static Result<void> do_stop(const BuiltinArguments& args) {
- auto lock = std::lock_guard{service_lock};
Service* svc = ServiceList::GetInstance().FindService(args[1]);
if (!svc) return Error() << "service " << args[1] << " not found";
svc->Stop();
@@ -768,7 +762,6 @@
}
static Result<void> do_restart(const BuiltinArguments& args) {
- auto lock = std::lock_guard{service_lock};
Service* svc = ServiceList::GetInstance().FindService(args[1]);
if (!svc) return Error() << "service " << args[1] << " not found";
svc->Restart();
@@ -1078,11 +1071,12 @@
static Result<void> do_wait(const BuiltinArguments& args) {
auto timeout = kCommandRetryTimeout;
if (args.size() == 3) {
- int timeout_int;
- if (!android::base::ParseInt(args[2], &timeout_int)) {
+ double timeout_double;
+ if (!android::base::ParseDouble(args[2], &timeout_double, 0)) {
return Error() << "failed to parse timeout";
}
- timeout = std::chrono::seconds(timeout_int);
+ timeout = std::chrono::duration_cast<std::chrono::nanoseconds>(
+ std::chrono::duration<double>(timeout_double));
}
if (wait_for_file(args[1].c_str(), timeout) != 0) {
@@ -1124,7 +1118,6 @@
function(StringPrintf("Exec service failed, status %d", siginfo.si_status));
}
});
- auto lock = std::lock_guard{service_lock};
if (auto result = (*service)->ExecStart(); !result.ok()) {
function("ExecStart failed: " + result.error().message());
}
@@ -1264,7 +1257,6 @@
}
success &= parser.ParseConfigFile(c);
}
- auto lock = std::lock_guard{service_lock};
ServiceList::GetInstance().MarkServicesUpdate();
if (success) {
return {};
@@ -1362,11 +1354,11 @@
// mount_all is currently too complex to run in vendor_init as it queues action triggers,
// imports rc scripts, etc. It should be simplified and run in vendor_init context.
// mount and umount are run in the same context as mount_all for symmetry.
- {"mount_all", {1, kMax, {false, do_mount_all}}},
+ {"mount_all", {0, kMax, {false, do_mount_all}}},
{"mount", {3, kMax, {false, do_mount}}},
{"perform_apex_config", {0, 0, {false, do_perform_apex_config}}},
{"umount", {1, 1, {false, do_umount}}},
- {"umount_all", {1, 1, {false, do_umount_all}}},
+ {"umount_all", {0, 1, {false, do_umount_all}}},
{"update_linker_config", {0, 0, {false, do_update_linker_config}}},
{"readahead", {1, 2, {true, do_readahead}}},
{"remount_userdata", {0, 0, {false, do_remount_userdata}}},
diff --git a/init/check_builtins.cpp b/init/check_builtins.cpp
index d62ecb0..450c079 100644
--- a/init/check_builtins.cpp
+++ b/init/check_builtins.cpp
@@ -25,6 +25,7 @@
#include <sys/time.h>
#include <android-base/logging.h>
+#include <android-base/parsedouble.h>
#include <android-base/parseint.h>
#include <android-base/strings.h>
@@ -122,6 +123,14 @@
return {};
}
+Result<void> check_mount_all(const BuiltinArguments& args) {
+ auto options = ParseMountAll(args.args);
+ if (!options.ok()) {
+ return options.error();
+ }
+ return {};
+}
+
Result<void> check_mkdir(const BuiltinArguments& args) {
auto options = ParseMkdir(args.args);
if (!options.ok()) {
@@ -203,10 +212,18 @@
return {};
}
+Result<void> check_umount_all(const BuiltinArguments& args) {
+ auto options = ParseUmountAll(args.args);
+ if (!options.ok()) {
+ return options.error();
+ }
+ return {};
+}
+
Result<void> check_wait(const BuiltinArguments& args) {
if (args.size() == 3 && !args[2].empty()) {
- int timeout_int;
- if (!android::base::ParseInt(args[2], &timeout_int)) {
+ double timeout_double;
+ if (!android::base::ParseDouble(args[2], &timeout_double, 0)) {
return Error() << "failed to parse timeout";
}
}
diff --git a/init/check_builtins.h b/init/check_builtins.h
index fb34556..725a6fd 100644
--- a/init/check_builtins.h
+++ b/init/check_builtins.h
@@ -32,11 +32,13 @@
Result<void> check_load_system_props(const BuiltinArguments& args);
Result<void> check_loglevel(const BuiltinArguments& args);
Result<void> check_mkdir(const BuiltinArguments& args);
+Result<void> check_mount_all(const BuiltinArguments& args);
Result<void> check_restorecon(const BuiltinArguments& args);
Result<void> check_restorecon_recursive(const BuiltinArguments& args);
Result<void> check_setprop(const BuiltinArguments& args);
Result<void> check_setrlimit(const BuiltinArguments& args);
Result<void> check_sysclktz(const BuiltinArguments& args);
+Result<void> check_umount_all(const BuiltinArguments& args);
Result<void> check_wait(const BuiltinArguments& args);
Result<void> check_wait_for_prop(const BuiltinArguments& args);
diff --git a/init/first_stage_console.cpp b/init/first_stage_console.cpp
new file mode 100644
index 0000000..cfa0d99
--- /dev/null
+++ b/init/first_stage_console.cpp
@@ -0,0 +1,106 @@
+/*
+ * 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 "first_stage_console.h"
+
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <termios.h>
+
+#include <string>
+#include <thread>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+
+static void RunScript() {
+ LOG(INFO) << "Attempting to run /first_stage.sh...";
+ pid_t pid = fork();
+ if (pid != 0) {
+ int status;
+ waitpid(pid, &status, 0);
+ LOG(INFO) << "/first_stage.sh exited with status " << status;
+ return;
+ }
+ const char* path = "/system/bin/sh";
+ const char* args[] = {path, "/first_stage.sh", nullptr};
+ int rv = execv(path, const_cast<char**>(args));
+ LOG(ERROR) << "unable to execv /first_stage.sh, returned " << rv << " errno " << errno;
+}
+
+namespace android {
+namespace init {
+
+void StartConsole() {
+ if (mknod("/dev/console", S_IFCHR | 0600, makedev(5, 1))) {
+ PLOG(ERROR) << "unable to create /dev/console";
+ return;
+ }
+ pid_t pid = fork();
+ if (pid != 0) {
+ int status;
+ waitpid(pid, &status, 0);
+ LOG(ERROR) << "console shell exited with status " << status;
+ return;
+ }
+ int fd = -1;
+ int tries = 50; // should timeout after 5s
+ // The device driver for console may not be ready yet so retry for a while in case of failure.
+ while (tries--) {
+ fd = open("/dev/console", O_RDWR);
+ if (fd != -1) {
+ break;
+ }
+ std::this_thread::sleep_for(100ms);
+ }
+ if (fd == -1) {
+ LOG(ERROR) << "Could not open /dev/console, errno = " << errno;
+ _exit(127);
+ }
+ ioctl(fd, TIOCSCTTY, 0);
+ dup2(fd, STDIN_FILENO);
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+ close(fd);
+
+ RunScript();
+ const char* path = "/system/bin/sh";
+ const char* args[] = {path, nullptr};
+ int rv = execv(path, const_cast<char**>(args));
+ LOG(ERROR) << "unable to execv, returned " << rv << " errno " << errno;
+ _exit(127);
+}
+
+int FirstStageConsole(const std::string& cmdline) {
+ auto pos = cmdline.find("androidboot.first_stage_console=");
+ if (pos != std::string::npos) {
+ int val = 0;
+ if (sscanf(cmdline.c_str() + pos, "androidboot.first_stage_console=%d", &val) != 1) {
+ return FirstStageConsoleParam::DISABLED;
+ }
+ if (val <= FirstStageConsoleParam::MAX_PARAM_VALUE && val >= 0) {
+ return val;
+ }
+ }
+ return FirstStageConsoleParam::DISABLED;
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/service_lock.cpp b/init/first_stage_console.h
similarity index 73%
copy from init/service_lock.cpp
copy to init/first_stage_console.h
index 404d439..8f36a7c 100644
--- a/init/service_lock.cpp
+++ b/init/first_stage_console.h
@@ -14,12 +14,22 @@
* limitations under the License.
*/
-#include "service_lock.h"
+#pragma once
+
+#include <string>
namespace android {
namespace init {
-RecursiveMutex service_lock;
+enum FirstStageConsoleParam {
+ DISABLED = 0,
+ CONSOLE_ON_FAILURE = 1,
+ IGNORE_FAILURE = 2,
+ MAX_PARAM_VALUE = IGNORE_FAILURE,
+};
+
+void StartConsole();
+int FirstStageConsole(const std::string& cmdline);
} // namespace init
} // namespace android
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index bd71cb5..0215576 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -24,12 +24,11 @@
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
-#include <sys/wait.h>
+#include <sys/utsname.h>
#include <unistd.h>
#include <filesystem>
#include <string>
-#include <thread>
#include <vector>
#include <android-base/chrono_utils.h>
@@ -39,6 +38,7 @@
#include <private/android_filesystem_config.h>
#include "debug_ramdisk.h"
+#include "first_stage_console.h"
#include "first_stage_mount.h"
#include "reboot_utils.h"
#include "switch_root.h"
@@ -94,55 +94,83 @@
}
}
-void StartConsole() {
- if (mknod("/dev/console", S_IFCHR | 0600, makedev(5, 1))) {
- PLOG(ERROR) << "unable to create /dev/console";
- return;
- }
- pid_t pid = fork();
- if (pid != 0) {
- int status;
- waitpid(pid, &status, 0);
- LOG(ERROR) << "console shell exited with status " << status;
- return;
- }
- int fd = -1;
- int tries = 10;
- // The device driver for console may not be ready yet so retry for a while in case of failure.
- while (tries--) {
- fd = open("/dev/console", O_RDWR);
- if (fd != -1) {
- break;
- }
- std::this_thread::sleep_for(100ms);
- }
- if (fd == -1) {
- LOG(ERROR) << "Could not open /dev/console, errno = " << errno;
- _exit(127);
- }
- ioctl(fd, TIOCSCTTY, 0);
- dup2(fd, STDIN_FILENO);
- dup2(fd, STDOUT_FILENO);
- dup2(fd, STDERR_FILENO);
- close(fd);
-
- const char* path = "/system/bin/sh";
- const char* args[] = {path, nullptr};
- int rv = execv(path, const_cast<char**>(args));
- LOG(ERROR) << "unable to execv, returned " << rv << " errno " << errno;
- _exit(127);
-}
-
-bool FirstStageConsole(const std::string& cmdline) {
- return cmdline.find("androidboot.first_stage_console=1") != std::string::npos;
-}
-
bool ForceNormalBoot(const std::string& cmdline) {
return cmdline.find("androidboot.force_normal_boot=1") != std::string::npos;
}
} // namespace
+std::string GetModuleLoadList(bool recovery, const std::string& dir_path) {
+ auto module_load_file = "modules.load";
+ if (recovery) {
+ struct stat fileStat;
+ std::string recovery_load_path = dir_path + "/modules.load.recovery";
+ if (!stat(recovery_load_path.c_str(), &fileStat)) {
+ module_load_file = "modules.load.recovery";
+ }
+ }
+
+ return module_load_file;
+}
+
+#define MODULE_BASE_DIR "/lib/modules"
+bool LoadKernelModules(bool recovery, bool want_console) {
+ struct utsname uts;
+ if (uname(&uts)) {
+ LOG(FATAL) << "Failed to get kernel version.";
+ }
+ int major, minor;
+ if (sscanf(uts.release, "%d.%d", &major, &minor) != 2) {
+ LOG(FATAL) << "Failed to parse kernel version " << uts.release;
+ }
+
+ std::unique_ptr<DIR, decltype(&closedir)> base_dir(opendir(MODULE_BASE_DIR), closedir);
+ if (!base_dir) {
+ LOG(INFO) << "Unable to open /lib/modules, skipping module loading.";
+ return true;
+ }
+ dirent* entry;
+ std::vector<std::string> module_dirs;
+ while ((entry = readdir(base_dir.get()))) {
+ if (entry->d_type != DT_DIR) {
+ continue;
+ }
+ int dir_major, dir_minor;
+ if (sscanf(entry->d_name, "%d.%d", &dir_major, &dir_minor) != 2 || dir_major != major ||
+ dir_minor != minor) {
+ continue;
+ }
+ module_dirs.emplace_back(entry->d_name);
+ }
+
+ // Sort the directories so they are iterated over during module loading
+ // in a consistent order. Alphabetical sorting is fine here because the
+ // kernel version at the beginning of the directory name must match the
+ // current kernel version, so the sort only applies to a label that
+ // follows the kernel version, for example /lib/modules/5.4 vs.
+ // /lib/modules/5.4-gki.
+ std::sort(module_dirs.begin(), module_dirs.end());
+
+ for (const auto& module_dir : module_dirs) {
+ std::string dir_path = MODULE_BASE_DIR "/";
+ dir_path.append(module_dir);
+ Modprobe m({dir_path}, GetModuleLoadList(recovery, dir_path));
+ bool retval = m.LoadListedModules(!want_console);
+ int modules_loaded = m.GetModuleCount();
+ if (modules_loaded > 0) {
+ return retval;
+ }
+ }
+
+ Modprobe m({MODULE_BASE_DIR}, GetModuleLoadList(recovery, MODULE_BASE_DIR));
+ bool retval = m.LoadListedModules(!want_console);
+ int modules_loaded = m.GetModuleCount();
+ if (modules_loaded > 0) {
+ return retval;
+ }
+ return true;
+}
+
int FirstStageMain(int argc, char** argv) {
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
@@ -234,17 +262,17 @@
old_root_dir.reset();
}
- Modprobe m({"/lib/modules"});
- auto want_console = ALLOW_FIRST_STAGE_CONSOLE && FirstStageConsole(cmdline);
- if (!m.LoadListedModules(!want_console)) {
- if (want_console) {
+ auto want_console = ALLOW_FIRST_STAGE_CONSOLE ? FirstStageConsole(cmdline) : 0;
+
+ if (!LoadKernelModules(IsRecoveryMode() && !ForceNormalBoot(cmdline), want_console)) {
+ if (want_console != FirstStageConsoleParam::DISABLED) {
LOG(ERROR) << "Failed to load kernel modules, starting console";
} else {
LOG(FATAL) << "Failed to load kernel modules";
}
}
- if (want_console) {
+ if (want_console == FirstStageConsoleParam::CONSOLE_ON_FAILURE) {
StartConsole();
}
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 622e457..8eb2f97 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -42,6 +42,7 @@
#include <liblp/liblp.h>
#include <libsnapshot/snapshot.h>
+#include "block_dev_initializer.h"
#include "devices.h"
#include "switch_root.h"
#include "uevent.h"
@@ -84,11 +85,7 @@
bool InitDevices();
protected:
- ListenerAction HandleBlockDevice(const std::string& name, const Uevent&,
- std::set<std::string>* required_devices);
bool InitRequiredDevices(std::set<std::string> devices);
- bool InitMappedDevice(const std::string& verity_device);
- bool InitDeviceMapper();
bool CreateLogicalPartitions();
bool MountPartition(const Fstab::iterator& begin, bool erase_same_mounts,
Fstab::iterator* end = nullptr);
@@ -97,7 +94,7 @@
bool TrySwitchSystemAsRoot();
bool TrySkipMountingPartitions();
bool IsDmLinearEnabled();
- void GetDmLinearMetadataDevice(std::set<std::string>* devices);
+ void GetSuperDeviceName(std::set<std::string>* devices);
bool InitDmLinearBackingDevices(const android::fs_mgr::LpMetadata& metadata);
void UseDsuIfPresent();
// Reads all fstab.avb_keys from the ramdisk for first-stage mount.
@@ -106,8 +103,6 @@
// revocation check by DSU installation service.
void CopyDsuAvbKeys();
- ListenerAction UeventCallback(const Uevent& uevent, std::set<std::string>* required_devices);
-
// Pure virtual functions.
virtual bool GetDmVerityDevices(std::set<std::string>* devices) = 0;
virtual bool SetUpDmVerity(FstabEntry* fstab_entry) = 0;
@@ -116,10 +111,10 @@
bool dsu_not_on_userdata_ = false;
Fstab fstab_;
- std::string lp_metadata_partition_;
+ // The super path is only set after InitDevices, and is invalid before.
+ std::string super_path_;
std::string super_partition_name_;
- std::unique_ptr<DeviceHandler> device_handler_;
- UeventListener uevent_listener_;
+ BlockDevInitializer block_dev_init_;
// Reads all AVB keys before chroot into /system, as they might be used
// later when mounting other partitions, e.g., /vendor and /product.
std::map<std::string, std::vector<std::string>> preload_avb_key_blobs_;
@@ -233,13 +228,7 @@
// Class Definitions
// -----------------
-FirstStageMount::FirstStageMount(Fstab fstab)
- : need_dm_verity_(false), fstab_(std::move(fstab)), uevent_listener_(16 * 1024 * 1024) {
- auto boot_devices = android::fs_mgr::GetBootDevices();
- device_handler_ = std::make_unique<DeviceHandler>(
- std::vector<Permissions>{}, std::vector<SysfsPermissions>{}, std::vector<Subsystem>{},
- std::move(boot_devices), false);
-
+FirstStageMount::FirstStageMount(Fstab fstab) : need_dm_verity_(false), fstab_(std::move(fstab)) {
super_partition_name_ = fs_mgr_get_super_partition_name();
}
@@ -268,12 +257,23 @@
bool FirstStageMount::InitDevices() {
std::set<std::string> devices;
- GetDmLinearMetadataDevice(&devices);
+ GetSuperDeviceName(&devices);
if (!GetDmVerityDevices(&devices)) {
return false;
}
- return InitRequiredDevices(std::move(devices));
+ if (!InitRequiredDevices(std::move(devices))) {
+ return false;
+ }
+
+ if (IsDmLinearEnabled()) {
+ auto super_symlink = "/dev/block/by-name/"s + super_partition_name_;
+ if (!android::base::Realpath(super_symlink, &super_path_)) {
+ PLOG(ERROR) << "realpath failed: " << super_symlink;
+ return false;
+ }
+ }
+ return true;
}
bool FirstStageMount::IsDmLinearEnabled() {
@@ -283,7 +283,7 @@
return false;
}
-void FirstStageMount::GetDmLinearMetadataDevice(std::set<std::string>* devices) {
+void FirstStageMount::GetSuperDeviceName(std::set<std::string>* devices) {
// Add any additional devices required for dm-linear mappings.
if (!IsDmLinearEnabled()) {
return;
@@ -296,62 +296,13 @@
// Found partitions will then be removed from it for the subsequent member
// function to check which devices are NOT created.
bool FirstStageMount::InitRequiredDevices(std::set<std::string> devices) {
- if (!InitDeviceMapper()) {
+ if (!block_dev_init_.InitDeviceMapper()) {
return false;
}
-
if (devices.empty()) {
return true;
}
-
- auto uevent_callback = [&, this](const Uevent& uevent) {
- return UeventCallback(uevent, &devices);
- };
- uevent_listener_.RegenerateUevents(uevent_callback);
-
- // UeventCallback() will remove found partitions from |devices|. So if it
- // isn't empty here, it means some partitions are not found.
- if (!devices.empty()) {
- LOG(INFO) << __PRETTY_FUNCTION__
- << ": partition(s) not found in /sys, waiting for their uevent(s): "
- << android::base::Join(devices, ", ");
- Timer t;
- uevent_listener_.Poll(uevent_callback, 10s);
- LOG(INFO) << "Wait for partitions returned after " << t;
- }
-
- if (!devices.empty()) {
- LOG(ERROR) << __PRETTY_FUNCTION__ << ": partition(s) not found after polling timeout: "
- << android::base::Join(devices, ", ");
- return false;
- }
-
- return true;
-}
-
-bool FirstStageMount::InitDeviceMapper() {
- const std::string dm_path = "/devices/virtual/misc/device-mapper";
- bool found = false;
- auto dm_callback = [this, &dm_path, &found](const Uevent& uevent) {
- if (uevent.path == dm_path) {
- device_handler_->HandleUevent(uevent);
- found = true;
- return ListenerAction::kStop;
- }
- return ListenerAction::kContinue;
- };
- uevent_listener_.RegenerateUeventsForPath("/sys" + dm_path, dm_callback);
- if (!found) {
- LOG(INFO) << "device-mapper device not found in /sys, waiting for its uevent";
- Timer t;
- uevent_listener_.Poll(dm_callback, 10s);
- LOG(INFO) << "Wait for device-mapper returned after " << t;
- }
- if (!found) {
- LOG(ERROR) << "device-mapper device not found after polling timeout";
- return false;
- }
- return true;
+ return block_dev_init_.InitDevices(std::move(devices));
}
bool FirstStageMount::InitDmLinearBackingDevices(const android::fs_mgr::LpMetadata& metadata) {
@@ -375,7 +326,7 @@
if (!IsDmLinearEnabled()) {
return true;
}
- if (lp_metadata_partition_.empty()) {
+ if (super_path_.empty()) {
LOG(ERROR) << "Could not locate logical partition tables in partition "
<< super_partition_name_;
return false;
@@ -392,92 +343,19 @@
if (!InitRequiredDevices({"userdata"})) {
return false;
}
- return sm->CreateLogicalAndSnapshotPartitions(lp_metadata_partition_);
+ return sm->CreateLogicalAndSnapshotPartitions(super_path_);
}
}
- auto metadata = android::fs_mgr::ReadCurrentMetadata(lp_metadata_partition_);
+ auto metadata = android::fs_mgr::ReadCurrentMetadata(super_path_);
if (!metadata) {
- LOG(ERROR) << "Could not read logical partition metadata from " << lp_metadata_partition_;
+ LOG(ERROR) << "Could not read logical partition metadata from " << super_path_;
return false;
}
if (!InitDmLinearBackingDevices(*metadata.get())) {
return false;
}
- return android::fs_mgr::CreateLogicalPartitions(*metadata.get(), lp_metadata_partition_);
-}
-
-ListenerAction FirstStageMount::HandleBlockDevice(const std::string& name, const Uevent& uevent,
- std::set<std::string>* required_devices) {
- // Matches partition name to create device nodes.
- // Both required_devices_partition_names_ and uevent->partition_name have A/B
- // suffix when A/B is used.
- auto iter = required_devices->find(name);
- if (iter != required_devices->end()) {
- LOG(VERBOSE) << __PRETTY_FUNCTION__ << ": found partition: " << *iter;
- if (IsDmLinearEnabled() && name == super_partition_name_) {
- std::vector<std::string> links = device_handler_->GetBlockDeviceSymlinks(uevent);
- lp_metadata_partition_ = links[0];
- }
- required_devices->erase(iter);
- device_handler_->HandleUevent(uevent);
- if (required_devices->empty()) {
- return ListenerAction::kStop;
- } else {
- return ListenerAction::kContinue;
- }
- }
- return ListenerAction::kContinue;
-}
-
-ListenerAction FirstStageMount::UeventCallback(const Uevent& uevent,
- std::set<std::string>* required_devices) {
- // Ignores everything that is not a block device.
- if (uevent.subsystem != "block") {
- return ListenerAction::kContinue;
- }
-
- if (!uevent.partition_name.empty()) {
- return HandleBlockDevice(uevent.partition_name, uevent, required_devices);
- } else {
- size_t base_idx = uevent.path.rfind('/');
- if (base_idx != std::string::npos) {
- return HandleBlockDevice(uevent.path.substr(base_idx + 1), uevent, required_devices);
- }
- }
- // Not found a partition or find an unneeded partition, continue to find others.
- return ListenerAction::kContinue;
-}
-
-// Creates "/dev/block/dm-XX" for dm-verity by running coldboot on /sys/block/dm-XX.
-bool FirstStageMount::InitMappedDevice(const std::string& dm_device) {
- const std::string device_name(basename(dm_device.c_str()));
- const std::string syspath = "/sys/block/" + device_name;
- bool found = false;
-
- auto verity_callback = [&device_name, &dm_device, this, &found](const Uevent& uevent) {
- if (uevent.device_name == device_name) {
- LOG(VERBOSE) << "Creating device-mapper device : " << dm_device;
- device_handler_->HandleUevent(uevent);
- found = true;
- return ListenerAction::kStop;
- }
- return ListenerAction::kContinue;
- };
-
- uevent_listener_.RegenerateUeventsForPath(syspath, verity_callback);
- if (!found) {
- LOG(INFO) << "dm device '" << dm_device << "' not found in /sys, waiting for its uevent";
- Timer t;
- uevent_listener_.Poll(verity_callback, 10s);
- LOG(INFO) << "wait for dm device '" << dm_device << "' returned after " << t;
- }
- if (!found) {
- LOG(ERROR) << "dm device '" << dm_device << "' not found after polling timeout";
- return false;
- }
-
- return true;
+ return android::fs_mgr::CreateLogicalPartitions(*metadata.get(), super_path_);
}
bool FirstStageMount::MountPartition(const Fstab::iterator& begin, bool erase_same_mounts,
@@ -491,7 +369,7 @@
if (!fs_mgr_update_logical_partition(&(*begin))) {
return false;
}
- if (!InitMappedDevice(begin->blk_device)) {
+ if (!block_dev_init_.InitDmDevice(begin->blk_device)) {
return false;
}
}
@@ -658,7 +536,9 @@
auto init_devices = [this](std::set<std::string> devices) -> bool {
for (auto iter = devices.begin(); iter != devices.end();) {
if (android::base::StartsWith(*iter, "/dev/block/dm-")) {
- if (!InitMappedDevice(*iter)) return false;
+ if (!block_dev_init_.InitDmDevice(*iter)) {
+ return false;
+ }
iter = devices.erase(iter);
} else {
iter++;
@@ -774,7 +654,7 @@
// The exact block device name (fstab_rec->blk_device) is changed to
// "/dev/block/dm-XX". Needs to create it because ueventd isn't started in init
// first stage.
- return InitMappedDevice(fstab_entry->blk_device);
+ return block_dev_init_.InitDmDevice(fstab_entry->blk_device);
default:
return false;
}
@@ -894,7 +774,7 @@
// The exact block device name (fstab_rec->blk_device) is changed to
// "/dev/block/dm-XX". Needs to create it because ueventd isn't started in init
// first stage.
- return InitMappedDevice(fstab_entry->blk_device);
+ return block_dev_init_.InitDmDevice(fstab_entry->blk_device);
default:
return false;
}
diff --git a/init/host_init_verifier.cpp b/init/host_init_verifier.cpp
index 0bd4df4..ef9a451 100644
--- a/init/host_init_verifier.cpp
+++ b/init/host_init_verifier.cpp
@@ -32,6 +32,7 @@
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/strings.h>
+#include <generated_android_ids.h>
#include <hidl/metadata.h>
#include <property_info_serializer/property_info_serializer.h>
@@ -48,9 +49,6 @@
#include "service_list.h"
#include "service_parser.h"
-#define EXCLUDE_FS_CONFIG_STRUCTURES
-#include "generated_android_ids.h"
-
using namespace std::literals;
using android::base::ParseInt;
diff --git a/init/init.cpp b/init/init.cpp
index a530d18..631db8e 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -22,6 +22,7 @@
#include <signal.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/eventfd.h>
#include <sys/mount.h>
#include <sys/signalfd.h>
#include <sys/types.h>
@@ -45,6 +46,7 @@
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <backtrace/Backtrace.h>
#include <fs_avb/fs_avb.h>
#include <fs_mgr_vendor_overlay.h>
#include <keyutils.h>
@@ -74,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"
@@ -81,6 +84,7 @@
using namespace std::string_literals;
using android::base::boot_clock;
+using android::base::ConsumePrefix;
using android::base::GetProperty;
using android::base::ReadFileToString;
using android::base::SetProperty;
@@ -97,37 +101,40 @@
static int signal_fd = -1;
static int property_fd = -1;
-static std::unique_ptr<Subcontext> subcontext;
+struct PendingControlMessage {
+ std::string message;
+ std::string name;
+ pid_t pid;
+ int fd;
+};
+static std::mutex pending_control_messages_lock;
+static std::queue<PendingControlMessage> pending_control_messages;
// Init epolls various FDs to wait for various inputs. It previously waited on property changes
// with a blocking socket that contained the information related to the change, however, it was easy
// to fill that socket and deadlock the system. Now we use locks to handle the property changes
// directly in the property thread, however we still must wake the epoll to inform init that there
// is a change to process, so we use this FD. It is non-blocking, since we do not care how many
-// times WakeEpoll() is called, only that the epoll will wake.
-static int wake_epoll_fd = -1;
+// times WakeMainInitThread() is called, only that the epoll will wake.
+static int wake_main_thread_fd = -1;
static void InstallInitNotifier(Epoll* epoll) {
- int sockets[2];
- if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, sockets) != 0) {
- PLOG(FATAL) << "Failed to socketpair() between property_service and init";
+ wake_main_thread_fd = eventfd(0, EFD_CLOEXEC);
+ if (wake_main_thread_fd == -1) {
+ PLOG(FATAL) << "Failed to create eventfd for waking init";
}
- int epoll_fd = sockets[0];
- wake_epoll_fd = sockets[1];
-
- auto drain_socket = [epoll_fd] {
- char buf[512];
- while (read(epoll_fd, buf, sizeof(buf)) > 0) {
- }
+ auto clear_eventfd = [] {
+ uint64_t counter;
+ TEMP_FAILURE_RETRY(read(wake_main_thread_fd, &counter, sizeof(counter)));
};
- if (auto result = epoll->RegisterHandler(epoll_fd, drain_socket); !result.ok()) {
+ if (auto result = epoll->RegisterHandler(wake_main_thread_fd, clear_eventfd); !result.ok()) {
LOG(FATAL) << result.error();
}
}
-static void WakeEpoll() {
- constexpr char value[] = "1";
- write(wake_epoll_fd, value, sizeof(value));
+static void WakeMainInitThread() {
+ uint64_t counter = 1;
+ TEMP_FAILURE_RETRY(write(wake_main_thread_fd, &counter, sizeof(counter)));
}
static class PropWaiterState {
@@ -171,7 +178,7 @@
LOG(INFO) << "Wait for property '" << wait_prop_name_ << "=" << wait_prop_value_
<< "' took " << *waiting_for_prop_;
ResetWaitForPropLocked();
- WakeEpoll();
+ WakeMainInitThread();
}
}
}
@@ -219,26 +226,52 @@
auto lock = std::lock_guard{shutdown_command_lock_};
shutdown_command_ = command;
do_shutdown_ = true;
- WakeEpoll();
+ WakeMainInitThread();
}
std::optional<std::string> CheckShutdown() {
auto lock = std::lock_guard{shutdown_command_lock_};
if (do_shutdown_ && !IsShuttingDown()) {
- do_shutdown_ = false;
return shutdown_command_;
}
return {};
}
+ bool do_shutdown() const { return do_shutdown_; }
+ void set_do_shutdown(bool value) { do_shutdown_ = value; }
+
private:
std::mutex shutdown_command_lock_;
std::string shutdown_command_;
bool do_shutdown_ = false;
} shutdown_state;
+static void UnwindMainThreadStack() {
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, 1));
+ if (!backtrace->Unwind(0)) {
+ LOG(ERROR) << __FUNCTION__ << "sys.powerctl: Failed to unwind callstack.";
+ }
+ for (size_t i = 0; i < backtrace->NumFrames(); i++) {
+ LOG(ERROR) << "sys.powerctl: " << backtrace->FormatFrameData(i);
+ }
+}
+
+void DebugRebootLogging() {
+ LOG(INFO) << "sys.powerctl: do_shutdown: " << shutdown_state.do_shutdown()
+ << " IsShuttingDown: " << IsShuttingDown();
+ if (shutdown_state.do_shutdown()) {
+ LOG(ERROR) << "sys.powerctl set while a previous shutdown command has not been handled";
+ UnwindMainThreadStack();
+ DumpShutdownDebugInformation();
+ }
+ if (IsShuttingDown()) {
+ LOG(ERROR) << "sys.powerctl set while init is already shutting down";
+ UnwindMainThreadStack();
+ DumpShutdownDebugInformation();
+ }
+}
+
void DumpState() {
- auto lock = std::lock_guard{service_lock};
ServiceList::GetInstance().DumpState();
ActionManager::GetInstance().DumpState();
}
@@ -247,9 +280,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;
@@ -259,9 +291,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;
}
@@ -304,7 +336,7 @@
if (property_triggers_enabled) {
ActionManager::GetInstance().QueuePropertyChange(name, value);
- WakeEpoll();
+ WakeMainInitThread();
}
prop_waiter_state.CheckAndResetWait(name, value);
@@ -312,7 +344,6 @@
static std::optional<boot_clock::time_point> HandleProcessActions() {
std::optional<boot_clock::time_point> next_process_action_time;
- auto lock = std::lock_guard{service_lock};
for (const auto& s : ServiceList::GetInstance()) {
if ((s->flags() & SVC_RUNNING) && s->timeout_period()) {
auto timeout_time = s->time_started() + *s->timeout_period();
@@ -341,7 +372,7 @@
return next_process_action_time;
}
-static Result<void> DoControlStart(Service* service) REQUIRES(service_lock) {
+static Result<void> DoControlStart(Service* service) {
return service->Start();
}
@@ -350,7 +381,7 @@
return {};
}
-static Result<void> DoControlRestart(Service* service) REQUIRES(service_lock) {
+static Result<void> DoControlRestart(Service* service) {
service->Restart();
return {};
}
@@ -360,39 +391,26 @@
INTERFACE, // action gets called for every service that holds this interface
};
-struct ControlMessageFunction {
- ControlTarget target;
- std::function<Result<void>(Service*)> action;
-};
+using ControlMessageFunction = std::function<Result<void>(Service*)>;
-static const std::map<std::string, ControlMessageFunction>& get_control_message_map() {
+static const std::map<std::string, ControlMessageFunction, std::less<>>& GetControlMessageMap() {
// clang-format off
- static const std::map<std::string, ControlMessageFunction> control_message_functions = {
- {"sigstop_on", {ControlTarget::SERVICE,
- [](auto* service) { service->set_sigstop(true); return Result<void>{}; }}},
- {"sigstop_off", {ControlTarget::SERVICE,
- [](auto* service) { service->set_sigstop(false); return Result<void>{}; }}},
- {"start", {ControlTarget::SERVICE, DoControlStart}},
- {"stop", {ControlTarget::SERVICE, DoControlStop}},
- {"restart", {ControlTarget::SERVICE, DoControlRestart}},
- {"interface_start", {ControlTarget::INTERFACE, DoControlStart}},
- {"interface_stop", {ControlTarget::INTERFACE, DoControlStop}},
- {"interface_restart", {ControlTarget::INTERFACE, DoControlRestart}},
+ static const std::map<std::string, ControlMessageFunction, std::less<>> control_message_functions = {
+ {"sigstop_on", [](auto* service) { service->set_sigstop(true); return Result<void>{}; }},
+ {"sigstop_off", [](auto* service) { service->set_sigstop(false); return Result<void>{}; }},
+ {"oneshot_on", [](auto* service) { service->set_oneshot(true); return Result<void>{}; }},
+ {"oneshot_off", [](auto* service) { service->set_oneshot(false); return Result<void>{}; }},
+ {"start", DoControlStart},
+ {"stop", DoControlStop},
+ {"restart", DoControlRestart},
};
// clang-format on
return control_message_functions;
}
-bool HandleControlMessage(const std::string& msg, const std::string& name, pid_t from_pid) {
- const auto& map = get_control_message_map();
- const auto it = map.find(msg);
-
- if (it == map.end()) {
- LOG(ERROR) << "Unknown control msg '" << msg << "'";
- return false;
- }
-
+static bool HandleControlMessage(std::string_view message, const std::string& name,
+ pid_t from_pid) {
std::string cmdline_path = StringPrintf("proc/%d/cmdline", from_pid);
std::string process_cmdline;
if (ReadFileToString(cmdline_path, &process_cmdline)) {
@@ -402,43 +420,78 @@
process_cmdline = "unknown process";
}
- const ControlMessageFunction& function = it->second;
-
- auto lock = std::lock_guard{service_lock};
-
- Service* svc = nullptr;
-
- switch (function.target) {
- case ControlTarget::SERVICE:
- svc = ServiceList::GetInstance().FindService(name);
- break;
- case ControlTarget::INTERFACE:
- svc = ServiceList::GetInstance().FindInterface(name);
- break;
- default:
- LOG(ERROR) << "Invalid function target from static map key ctl." << msg << ": "
- << static_cast<std::underlying_type<ControlTarget>::type>(function.target);
- return false;
+ Service* service = nullptr;
+ auto action = message;
+ if (ConsumePrefix(&action, "interface_")) {
+ service = ServiceList::GetInstance().FindInterface(name);
+ } else {
+ service = ServiceList::GetInstance().FindService(name);
}
- if (svc == nullptr) {
- LOG(ERROR) << "Control message: Could not find '" << name << "' for ctl." << msg
+ if (service == nullptr) {
+ LOG(ERROR) << "Control message: Could not find '" << name << "' for ctl." << message
<< " from pid: " << from_pid << " (" << process_cmdline << ")";
return false;
}
- if (auto result = function.action(svc); !result.ok()) {
- LOG(ERROR) << "Control message: Could not ctl." << msg << " for '" << name
+ const auto& map = GetControlMessageMap();
+ const auto it = map.find(action);
+ if (it == map.end()) {
+ LOG(ERROR) << "Unknown control msg '" << message << "'";
+ return false;
+ }
+ const auto& function = it->second;
+
+ if (auto result = function(service); !result.ok()) {
+ LOG(ERROR) << "Control message: Could not ctl." << message << " for '" << name
<< "' from pid: " << from_pid << " (" << process_cmdline
<< "): " << result.error();
return false;
}
- LOG(INFO) << "Control message: Processed ctl." << msg << " for '" << name
+ LOG(INFO) << "Control message: Processed ctl." << message << " for '" << name
<< "' from pid: " << from_pid << " (" << process_cmdline << ")";
return true;
}
+bool QueueControlMessage(const std::string& message, const std::string& name, pid_t pid, int fd) {
+ auto lock = std::lock_guard{pending_control_messages_lock};
+ if (pending_control_messages.size() > 100) {
+ LOG(ERROR) << "Too many pending control messages, dropped '" << message << "' for '" << name
+ << "' from pid: " << pid;
+ return false;
+ }
+ pending_control_messages.push({message, name, pid, fd});
+ WakeMainInitThread();
+ return true;
+}
+
+static void HandleControlMessages() {
+ auto lock = std::unique_lock{pending_control_messages_lock};
+ // Init historically would only execute handle one property message, including control messages
+ // in each iteration of its main loop. We retain this behavior here to prevent starvation of
+ // other actions in the main loop.
+ if (!pending_control_messages.empty()) {
+ auto control_message = pending_control_messages.front();
+ pending_control_messages.pop();
+ lock.unlock();
+
+ bool success = HandleControlMessage(control_message.message, control_message.name,
+ control_message.pid);
+
+ uint32_t response = success ? PROP_SUCCESS : PROP_ERROR_HANDLE_CONTROL_MESSAGE;
+ if (control_message.fd != -1) {
+ TEMP_FAILURE_RETRY(send(control_message.fd, &response, sizeof(response), 0));
+ close(control_message.fd);
+ }
+ lock.lock();
+ }
+ // If we still have items to process, make sure we wake back up to do so.
+ if (!pending_control_messages.empty()) {
+ WakeMainInitThread();
+ }
+}
+
static Result<void> wait_for_coldboot_done_action(const BuiltinArguments& args) {
if (!prop_waiter_state.StartWaiting(kColdBootDoneProp, "true")) {
LOG(FATAL) << "Could not wait for '" << kColdBootDoneProp << "'";
@@ -484,7 +537,9 @@
// Set the UDC controller for the ConfigFS USB Gadgets.
// Read the UDC controller in use from "/sys/class/udc".
// In case of multiple UDC controllers select the first one.
-static void set_usb_controller() {
+static void SetUsbController() {
+ static auto controller_set = false;
+ if (controller_set) return;
std::unique_ptr<DIR, decltype(&closedir)>dir(opendir("/sys/class/udc"), closedir);
if (!dir) return;
@@ -493,6 +548,7 @@
if (dp->d_name[0] == '.') continue;
SetProperty("sys.usb.controller", dp->d_name);
+ controller_set = true;
break;
}
}
@@ -588,7 +644,6 @@
}
auto found = false;
- auto lock = std::lock_guard{service_lock};
for (const auto& service : ServiceList::GetInstance()) {
auto svc = service.get();
if (svc->keycodes() == keycodes) {
@@ -659,22 +714,6 @@
}
}
-void SendStopSendingMessagesMessage() {
- auto init_message = InitMessage{};
- init_message.set_stop_sending_messages(true);
- if (auto result = SendMessage(property_fd, init_message); !result.ok()) {
- LOG(ERROR) << "Failed to send 'stop sending messages' message: " << result.error();
- }
-}
-
-void SendStartSendingMessagesMessage() {
- auto init_message = InitMessage{};
- init_message.set_start_sending_messages(true);
- if (auto result = SendMessage(property_fd, init_message); !result.ok()) {
- LOG(ERROR) << "Failed to send 'start sending messages' message: " << result.error();
- }
-}
-
int SecondStageMain(int argc, char** argv) {
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
@@ -685,11 +724,18 @@
trigger_shutdown = [](const std::string& command) { shutdown_state.TriggerShutdown(command); };
SetStdioToDevNull(argv);
- InitKernelLogging(argv);
+ InitSecondStageLogging(argv);
LOG(INFO) << "init second stage started!";
- // Will handle EPIPE at the time of write by checking the errno
- signal(SIGPIPE, SIG_IGN);
+ // Init should not crash because of a dependence on any other process, therefore we ignore
+ // SIGPIPE and handle EPIPE at the call site directly. Note that setting a signal to SIG_IGN
+ // is inherited across exec, but custom signal handlers are not. Since we do not want to
+ // ignore SIGPIPE for child processes, we set a no-op function for the signal handler instead.
+ {
+ struct sigaction action = {.sa_flags = SA_RESTART};
+ action.sa_handler = [](int) {};
+ sigaction(SIGPIPE, &action, nullptr);
+ }
// Set init and its forked children's oom_adj.
if (auto result =
@@ -757,7 +803,7 @@
fs_mgr_vendor_overlay_mount_all();
export_oem_lock_status();
MountHandler mount_handler(&epoll);
- set_usb_controller();
+ SetUsbController();
const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
Action::set_function_map(&function_map);
@@ -766,7 +812,7 @@
PLOG(FATAL) << "SetupMountNamespaces failed";
}
- subcontext = InitializeSubcontext();
+ InitializeSubcontext();
ActionManager& am = ActionManager::GetInstance();
ServiceList& sm = ServiceList::GetInstance();
@@ -778,11 +824,10 @@
if (false) DumpState();
// Make the GSI status available before scripts start running.
- if (android::gsi::IsGsiRunning()) {
- SetProperty("ro.gsid.image_running", "1");
- } else {
- SetProperty("ro.gsid.image_running", "0");
- }
+ auto is_running = android::gsi::IsGsiRunning() ? "1" : "0";
+ SetProperty(gsi::kGsiBootedProp, is_running);
+ auto is_installed = android::gsi::IsGsiInstalled() ? "1" : "0";
+ SetProperty(gsi::kGsiInstalledProp, is_installed);
am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
@@ -797,7 +842,6 @@
Keychords keychords;
am.QueueBuiltinAction(
[&epoll, &keychords](const BuiltinArguments& args) -> Result<void> {
- auto lock = std::lock_guard{service_lock};
for (const auto& svc : ServiceList::GetInstance()) {
keychords.Register(svc->keycodes());
}
@@ -830,7 +874,10 @@
auto shutdown_command = shutdown_state.CheckShutdown();
if (shutdown_command) {
+ LOG(INFO) << "Got shutdown_command '" << *shutdown_command
+ << "' Calling HandlePowerctlMessage()";
HandlePowerctlMessage(*shutdown_command);
+ shutdown_state.set_do_shutdown(false);
}
if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
@@ -864,6 +911,10 @@
(*function)();
}
}
+ if (!IsShuttingDown()) {
+ HandleControlMessages();
+ SetUsbController();
+ }
}
return 0;
diff --git a/init/init.h b/init/init.h
index bcf24e7..4f686cb 100644
--- a/init/init.h
+++ b/init/init.h
@@ -38,11 +38,11 @@
void ResetWaitForProp();
void SendLoadPersistentPropertiesMessage();
-void SendStopSendingMessagesMessage();
-void SendStartSendingMessagesMessage();
void PropertyChanged(const std::string& name, const std::string& value);
-bool HandleControlMessage(const std::string& msg, const std::string& name, pid_t from_pid);
+bool QueueControlMessage(const std::string& message, const std::string& name, pid_t pid, int fd);
+
+void DebugRebootLogging();
int SecondStageMain(int argc, char** argv);
diff --git a/init/init_test.cpp b/init/init_test.cpp
index 3053bd8..07b4724 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -167,7 +167,6 @@
ServiceList service_list;
TestInitText(init_script, BuiltinFunctionMap(), {}, &service_list);
- auto lock = std::lock_guard{service_lock};
ASSERT_EQ(1, std::distance(service_list.begin(), service_list.end()));
auto service = service_list.begin()->get();
@@ -240,6 +239,28 @@
EXPECT_EQ(6, num_executed);
}
+TEST(init, RejectsCriticalAndOneshotService) {
+ std::string init_script =
+ R"init(
+service A something
+ class first
+ critical
+ oneshot
+)init";
+
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ ASSERT_TRUE(android::base::WriteStringToFd(init_script, tf.fd));
+
+ ServiceList service_list;
+ Parser parser;
+ parser.AddSectionParser("service",
+ std::make_unique<ServiceParser>(&service_list, nullptr, std::nullopt));
+
+ ASSERT_TRUE(parser.ParseConfig(tf.path));
+ ASSERT_EQ(1u, parser.parse_error_count());
+}
+
} // namespace init
} // namespace android
diff --git a/init/lmkd_service.cpp b/init/lmkd_service.cpp
index a531d0a..dd1ab4d 100644
--- a/init/lmkd_service.cpp
+++ b/init/lmkd_service.cpp
@@ -79,8 +79,7 @@
}
static void RegisterServices(pid_t exclude_pid) {
- auto lock = std::lock_guard{service_lock};
- for (const auto& service : ServiceList::GetInstance()) {
+ for (const auto& service : ServiceList::GetInstance().services()) {
auto svc = service.get();
if (svc->oom_score_adjust() != DEFAULT_OOM_SCORE_ADJUST) {
// skip if process is excluded or not yet forked (pid==0)
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index 2175075..f3b584c 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -29,7 +29,6 @@
#include <android-base/unique_fd.h>
#include <apex_manifest.pb.h>
-#include "property_service.h"
#include "util.h"
namespace android {
@@ -241,14 +240,20 @@
// slave to the /mnt/user mount, and at the same time /mnt/installer in the
// bootstrap namespace shares a peer group with /mnt/installer in the
// default namespace.
+ // /mnt/androidwritable is similar to /mnt/installer but serves for
+ // MOUNT_EXTERNAL_ANDROID_WRITABLE apps.
if (!mkdir_recursive("/mnt/user", 0755)) return false;
if (!mkdir_recursive("/mnt/installer", 0755)) return false;
+ if (!mkdir_recursive("/mnt/androidwritable", 0755)) return false;
if (!(BindMount("/mnt/user", "/mnt/installer", true))) return false;
- // First, make /mnt/installer a slave bind mount
+ if (!(BindMount("/mnt/user", "/mnt/androidwritable", true))) return false;
+ // First, make /mnt/installer and /mnt/androidwritable a slave bind mount
if (!(MakeSlave("/mnt/installer"))) return false;
+ if (!(MakeSlave("/mnt/androidwritable"))) return false;
// Then, make it shared again - effectively creating a new peer group, that
// will be inherited by new mount namespaces.
if (!(MakeShared("/mnt/installer"))) return false;
+ if (!(MakeShared("/mnt/androidwritable"))) return false;
bootstrap_ns_fd.reset(OpenMountNamespace());
bootstrap_ns_id = GetMountNamespaceId();
@@ -291,14 +296,6 @@
return true;
}
if (default_ns_id != GetMountNamespaceId()) {
- // The property service thread and its descendent threads must be in the correct mount
- // namespace to call Service::Start(), however setns() only operates on a single thread and
- // fails when secondary threads attempt to join the same mount namespace. Therefore, we
- // must join the property service thread and its descendents before the setns() call. Those
- // threads are then started again after the setns() call, and they'll be in the proper
- // namespace.
- PausePropertyService();
-
if (setns(default_ns_fd.get(), CLONE_NEWNS) == -1) {
PLOG(ERROR) << "Failed to switch back to the default mount namespace.";
return false;
@@ -308,8 +305,6 @@
LOG(ERROR) << result.error();
return false;
}
-
- ResumePropertyService();
}
LOG(INFO) << "Switched to default mount namespace";
@@ -323,20 +318,10 @@
}
if (bootstrap_ns_id != GetMountNamespaceId() && bootstrap_ns_fd.get() != -1 &&
IsApexUpdatable()) {
- // The property service thread and its descendent threads must be in the correct mount
- // namespace to call Service::Start(), however setns() only operates on a single thread and
- // fails when secondary threads attempt to join the same mount namespace. Therefore, we
- // must join the property service thread and its descendents before the setns() call. Those
- // threads are then started again after the setns() call, and they'll be in the proper
- // namespace.
- PausePropertyService();
-
if (setns(bootstrap_ns_fd.get(), CLONE_NEWNS) == -1) {
PLOG(ERROR) << "Failed to switch to bootstrap mount namespace.";
return false;
}
-
- ResumePropertyService();
}
return true;
}
diff --git a/init/oneshot_on_test.cpp b/init/oneshot_on_test.cpp
new file mode 100644
index 0000000..650f065
--- /dev/null
+++ b/init/oneshot_on_test.cpp
@@ -0,0 +1,49 @@
+/*
+ * 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 <gtest/gtest.h>
+
+#include <chrono>
+
+#include <android-base/properties.h>
+
+using android::base::GetProperty;
+using android::base::SetProperty;
+using android::base::WaitForProperty;
+using namespace std::literals;
+
+TEST(init, oneshot_on) {
+ if (getuid() != 0) {
+ GTEST_SKIP() << "Skipping test, must be run as root.";
+ return;
+ }
+
+ // Bootanim shouldn't be running once the device has booted.
+ ASSERT_EQ("stopped", GetProperty("init.svc.bootanim", ""));
+
+ SetProperty("ctl.oneshot_off", "bootanim");
+ SetProperty("ctl.start", "bootanim");
+
+ // Bootanim exits quickly when the device is fully booted, so check that it goes back to the
+ // 'restarting' state that non-oneshot services enter once they've restarted.
+ EXPECT_TRUE(WaitForProperty("init.svc.bootanim", "restarting", 10s));
+
+ SetProperty("ctl.oneshot_on", "bootanim");
+ SetProperty("ctl.start", "bootanim");
+
+ // Now that oneshot is enabled again, bootanim should transition into the 'stopped' state.
+ EXPECT_TRUE(WaitForProperty("init.svc.bootanim", "stopped", 10s));
+}
diff --git a/init/parser.cpp b/init/parser.cpp
index 507ee4a..5c18551 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -41,7 +41,7 @@
}
void Parser::ParseData(const std::string& filename, std::string* data) {
- data->push_back('\n'); // TODO: fix tokenizer
+ data->push_back('\n');
data->push_back('\0');
parse_state state;
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 319a241..82f5b8c 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -47,6 +47,7 @@
#include <thread>
#include <vector>
+#include <InitProperties.sysprop.h>
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -85,6 +86,7 @@
using android::properties::ParsePropertyInfoFile;
using android::properties::PropertyInfoAreaFile;
using android::properties::PropertyInfoEntry;
+using android::sysprop::InitProperties::is_userspace_reboot_supported;
namespace android {
namespace init {
@@ -95,6 +97,7 @@
static int from_init_socket = -1;
static int init_socket = -1;
static bool accept_messages = false;
+static std::mutex accept_messages_lock;
static std::thread property_service_thread;
static PropertyInfoAreaFile property_info_area;
@@ -117,6 +120,16 @@
return 0;
}
+void StartSendingMessages() {
+ auto lock = std::lock_guard{accept_messages_lock};
+ accept_messages = true;
+}
+
+void StopSendingMessages() {
+ auto lock = std::lock_guard{accept_messages_lock};
+ accept_messages = false;
+}
+
bool CanReadProperty(const std::string& source_context, const std::string& name) {
const char* target_context = nullptr;
property_info_area->GetPropertyInfo(name.c_str(), &target_context, nullptr);
@@ -186,138 +199,49 @@
}
// If init hasn't started its main loop, then it won't be handling property changed messages
// anyway, so there's no need to try to send them.
+ auto lock = std::lock_guard{accept_messages_lock};
if (accept_messages) {
PropertyChanged(name, value);
}
return PROP_SUCCESS;
}
-template <typename T>
-class SingleThreadExecutor {
+class AsyncRestorecon {
public:
- virtual ~SingleThreadExecutor() {}
-
- template <typename F = T>
- void Run(F&& item) {
+ void TriggerRestorecon(const std::string& path) {
auto guard = std::lock_guard{mutex_};
- items_.emplace(std::forward<F>(item));
+ paths_.emplace(path);
- if (thread_state_ == ThreadState::kRunning || thread_state_ == ThreadState::kStopped) {
- return;
- }
-
- if (thread_state_ == ThreadState::kPendingJoin) {
- thread_.join();
- }
-
- StartThread();
- }
-
- void StopAndJoin() {
- auto lock = std::unique_lock{mutex_};
- if (thread_state_ == ThreadState::kPendingJoin) {
- thread_.join();
- } else if (thread_state_ == ThreadState::kRunning) {
- thread_state_ = ThreadState::kStopped;
- lock.unlock();
- thread_.join();
- lock.lock();
- }
-
- thread_state_ = ThreadState::kStopped;
- }
-
- void Restart() {
- auto guard = std::lock_guard{mutex_};
- if (items_.empty()) {
- thread_state_ = ThreadState::kNotStarted;
- } else {
- StartThread();
- }
- }
-
- void MaybeJoin() {
- auto guard = std::lock_guard{mutex_};
- if (thread_state_ == ThreadState::kPendingJoin) {
- thread_.join();
- thread_state_ = ThreadState::kNotStarted;
+ if (!thread_started_) {
+ thread_started_ = true;
+ std::thread{&AsyncRestorecon::ThreadFunction, this}.detach();
}
}
private:
- virtual void Execute(T&& item) = 0;
-
- void StartThread() {
- thread_state_ = ThreadState::kRunning;
- auto thread = std::thread{&SingleThreadExecutor::ThreadFunction, this};
- std::swap(thread_, thread);
- }
-
void ThreadFunction() {
auto lock = std::unique_lock{mutex_};
- while (!items_.empty()) {
- auto item = items_.front();
- items_.pop();
+ while (!paths_.empty()) {
+ auto path = paths_.front();
+ paths_.pop();
lock.unlock();
- Execute(std::move(item));
+ if (selinux_android_restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
+ LOG(ERROR) << "Asynchronous restorecon of '" << path << "' failed'";
+ }
+ android::base::SetProperty(kRestoreconProperty, path);
lock.lock();
}
- if (thread_state_ != ThreadState::kStopped) {
- thread_state_ = ThreadState::kPendingJoin;
- }
+ thread_started_ = false;
}
std::mutex mutex_;
- std::queue<T> items_;
- enum class ThreadState {
- kNotStarted, // Initial state when starting the program or when restarting with no items to
- // process.
- kRunning, // The thread is running and is in a state that it will process new items if
- // are run.
- kPendingJoin, // The thread has run to completion and is pending join(). A new thread must
- // be launched for new items to be processed.
- kStopped, // This executor has stopped and will not process more items until Restart() is
- // called. Currently pending items will be processed and the thread will be
- // joined.
- };
- ThreadState thread_state_ = ThreadState::kNotStarted;
- std::thread thread_;
+ std::queue<std::string> paths_;
+ bool thread_started_ = false;
};
-class RestoreconThread : public SingleThreadExecutor<std::string> {
- virtual void Execute(std::string&& path) override {
- if (selinux_android_restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
- LOG(ERROR) << "Asynchronous restorecon of '" << path << "' failed'";
- }
- android::base::SetProperty(kRestoreconProperty, path);
- }
-};
-
-struct ControlMessageInfo {
- std::string message;
- std::string name;
- pid_t pid;
- int fd;
-};
-
-class ControlMessageThread : public SingleThreadExecutor<ControlMessageInfo> {
- virtual void Execute(ControlMessageInfo&& info) override {
- bool success = HandleControlMessage(info.message, info.name, info.pid);
-
- uint32_t response = success ? PROP_SUCCESS : PROP_ERROR_HANDLE_CONTROL_MESSAGE;
- if (info.fd != -1) {
- TEMP_FAILURE_RETRY(send(info.fd, &response, sizeof(response), 0));
- close(info.fd);
- }
- }
-};
-
-static RestoreconThread restorecon_thread;
-static ControlMessageThread control_message_thread;
-
class SocketConnection {
public:
SocketConnection(int socket, const ucred& cred) : socket_(socket), cred_(cred) {}
@@ -454,22 +378,25 @@
static uint32_t SendControlMessage(const std::string& msg, const std::string& name, pid_t pid,
SocketConnection* socket, std::string* error) {
+ auto lock = std::lock_guard{accept_messages_lock};
if (!accept_messages) {
*error = "Received control message after shutdown, ignoring";
return PROP_ERROR_HANDLE_CONTROL_MESSAGE;
}
- // We must release the fd before spawning the thread, otherwise there will be a race with the
- // thread. If the thread calls close() before this function calls Release(), then fdsan will see
- // the wrong tag and abort().
+ // We must release the fd before sending it to init, otherwise there will be a race with init.
+ // If init calls close() before Release(), then fdsan will see the wrong tag and abort().
int fd = -1;
if (socket != nullptr && SelinuxGetVendorAndroidVersion() > __ANDROID_API_Q__) {
fd = socket->Release();
}
- // Handling a control message likely calls SetProperty, which we must synchronously handle,
- // therefore we must fork a thread to handle it.
- control_message_thread.Run({msg, name, pid, fd});
+ bool queue_success = QueueControlMessage(msg, name, pid, fd);
+ if (!queue_success && fd != -1) {
+ uint32_t response = PROP_ERROR_HANDLE_CONTROL_MESSAGE;
+ TEMP_FAILURE_RETRY(send(fd, &response, sizeof(response), 0));
+ close(fd);
+ }
return PROP_SUCCESS;
}
@@ -564,6 +491,13 @@
}
LOG(INFO) << "Received sys.powerctl='" << value << "' from pid: " << cr.pid
<< process_log_string;
+ if (!value.empty()) {
+ DebugRebootLogging();
+ }
+ if (value == "reboot,userspace" && !is_userspace_reboot_supported().value_or(false)) {
+ *error = "Userspace reboot is not supported by this device";
+ return PROP_ERROR_INVALID_VALUE;
+ }
}
// If a process other than init is writing a non-empty value, it means that process is
@@ -571,7 +505,8 @@
// We use a thread to do this restorecon operation to prevent holding up init, as it may take
// a long time to complete.
if (name == kRestoreconProperty && cr.pid != 1 && !value.empty()) {
- restorecon_thread.Run(value);
+ static AsyncRestorecon async_restorecon;
+ async_restorecon.TriggerRestorecon(value);
return PROP_SUCCESS;
}
@@ -942,28 +877,32 @@
}
void PropertyLoadBootDefaults() {
- // TODO(b/117892318): merge prop.default and build.prop files into one
// We read the properties and their values into a map, in order to always allow properties
// loaded in the later property files to override the properties in loaded in the earlier
// property files, regardless of if they are "ro." properties or not.
std::map<std::string, std::string> properties;
- if (!load_properties_from_file("/system/etc/prop.default", nullptr, &properties)) {
- // Try recovery path
- if (!load_properties_from_file("/prop.default", nullptr, &properties)) {
- // Try legacy path
- load_properties_from_file("/default.prop", nullptr, &properties);
- }
+
+ if (IsRecoveryMode()) {
+ load_properties_from_file("/prop.default", nullptr, &properties);
}
+
load_properties_from_file("/system/build.prop", nullptr, &properties);
load_properties_from_file("/system_ext/build.prop", nullptr, &properties);
- load_properties_from_file("/vendor/default.prop", nullptr, &properties);
+
+ // TODO(b/117892318): uncomment the following condition when vendor.imgs for
+ // aosp_* targets are all updated.
+// if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_R__) {
+ load_properties_from_file("/vendor/default.prop", nullptr, &properties);
+// }
load_properties_from_file("/vendor/build.prop", nullptr, &properties);
+
if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_Q__) {
load_properties_from_file("/odm/etc/build.prop", nullptr, &properties);
} else {
load_properties_from_file("/odm/default.prop", nullptr, &properties);
load_properties_from_file("/odm/build.prop", nullptr, &properties);
}
+
load_properties_from_file("/product/build.prop", nullptr, &properties);
load_properties_from_file("/factory/factory.prop", "ro.*", &properties);
@@ -1152,8 +1091,6 @@
PropertyLoadBootDefaults();
}
-static bool pause_property_service = false;
-
static void HandleInitSocket() {
auto message = ReadMessage(init_socket);
if (!message.ok()) {
@@ -1180,18 +1117,6 @@
persistent_properties_loaded = true;
break;
}
- case InitMessage::kStopSendingMessages: {
- accept_messages = false;
- break;
- }
- case InitMessage::kStartSendingMessages: {
- accept_messages = true;
- break;
- }
- case InitMessage::kPausePropertyService: {
- pause_property_service = true;
- break;
- }
default:
LOG(ERROR) << "Unknown message type from init: " << init_message.msg_case();
}
@@ -1212,7 +1137,7 @@
LOG(FATAL) << result.error();
}
- while (!pause_property_service) {
+ while (true) {
auto pending_functions = epoll.Wait(std::nullopt);
if (!pending_functions.ok()) {
LOG(ERROR) << pending_functions.error();
@@ -1221,34 +1146,9 @@
(*function)();
}
}
- control_message_thread.MaybeJoin();
- restorecon_thread.MaybeJoin();
}
}
-void SendStopPropertyServiceMessage() {
- auto init_message = InitMessage{};
- init_message.set_pause_property_service(true);
- if (auto result = SendMessage(from_init_socket, init_message); !result.ok()) {
- LOG(ERROR) << "Failed to send stop property service message: " << result.error();
- }
-}
-
-void PausePropertyService() {
- control_message_thread.StopAndJoin();
- restorecon_thread.StopAndJoin();
- SendStopPropertyServiceMessage();
- property_service_thread.join();
-}
-
-void ResumePropertyService() {
- pause_property_service = false;
- auto new_thread = std::thread{PropertyServiceThread};
- property_service_thread.swap(new_thread);
- restorecon_thread.Restart();
- control_message_thread.Restart();
-}
-
void StartPropertyService(int* epoll_socket) {
InitPropertySet("ro.property_service.version", "2");
@@ -1258,7 +1158,7 @@
}
*epoll_socket = from_init_socket = sockets[0];
init_socket = sockets[1];
- accept_messages = true;
+ StartSendingMessages();
if (auto result = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
false, 0666, 0, 0, {});
diff --git a/init/property_service.h b/init/property_service.h
index e921326..2d49a36 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -31,8 +31,9 @@
void PropertyInit();
void StartPropertyService(int* epoll_socket);
-void ResumePropertyService();
-void PausePropertyService();
+
+void StartSendingMessages();
+void StopSendingMessages();
} // namespace init
} // namespace android
diff --git a/init/property_service.proto b/init/property_service.proto
index 36245b2..08268d9 100644
--- a/init/property_service.proto
+++ b/init/property_service.proto
@@ -41,6 +41,5 @@
bool load_persistent_properties = 1;
bool stop_sending_messages = 2;
bool start_sending_messages = 3;
- bool pause_property_service = 4;
};
}
diff --git a/init/property_service_test.cpp b/init/property_service_test.cpp
index 0f4cd0d..c6dcfa2 100644
--- a/init/property_service_test.cpp
+++ b/init/property_service_test.cpp
@@ -22,8 +22,10 @@
#include <sys/_system_properties.h>
#include <android-base/properties.h>
+#include <android-base/scopeguard.h>
#include <gtest/gtest.h>
+using android::base::GetProperty;
using android::base::SetProperty;
namespace android {
@@ -74,5 +76,19 @@
EXPECT_TRUE(SetProperty("property_service_utf8_test", "\xF0\x90\x80\x80"));
}
+TEST(property_service, userspace_reboot_not_supported) {
+ if (getuid() != 0) {
+ GTEST_SKIP() << "Skipping test, must be run as root.";
+ return;
+ }
+ const std::string original_value = GetProperty("init.userspace_reboot.is_supported", "");
+ auto guard = android::base::make_scope_guard([&original_value]() {
+ SetProperty("init.userspace_reboot.is_supported", original_value);
+ });
+
+ ASSERT_TRUE(SetProperty("init.userspace_reboot.is_supported", "false"));
+ EXPECT_FALSE(SetProperty("sys.powerctl", "reboot,userspace"));
+}
+
} // namespace init
} // namespace android
diff --git a/init/reboot.cpp b/init/reboot.cpp
index cad192d..ffd58a3 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -59,18 +59,18 @@
#include "builtin_arguments.h"
#include "init.h"
#include "mount_namespace.h"
+#include "property_service.h"
#include "reboot_utils.h"
#include "service.h"
#include "service_list.h"
#include "sigchld_handler.h"
#include "util.h"
-#define PROC_SYSRQ "/proc/sysrq-trigger"
-
using namespace std::literals;
using android::base::boot_clock;
using android::base::GetBoolProperty;
+using android::base::GetUintProperty;
using android::base::SetProperty;
using android::base::Split;
using android::base::Timer;
@@ -85,7 +85,7 @@
static const std::set<std::string> kDebuggingServices{"tombstoned", "logd", "adbd", "console"};
-static std::vector<Service*> GetDebuggingServices(bool only_post_data) REQUIRES(service_lock) {
+static std::vector<Service*> GetDebuggingServices(bool only_post_data) {
std::vector<Service*> ret;
ret.reserve(kDebuggingServices.size());
for (const auto& s : ServiceList::GetInstance()) {
@@ -100,7 +100,15 @@
if (write_to_property) {
SetProperty(LAST_REBOOT_REASON_PROPERTY, reason);
}
- WriteStringToFile(reason, LAST_REBOOT_REASON_FILE);
+ auto fd = unique_fd(TEMP_FAILURE_RETRY(open(
+ LAST_REBOOT_REASON_FILE, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY, 0666)));
+ if (!fd.ok()) {
+ PLOG(ERROR) << "Could not open '" << LAST_REBOOT_REASON_FILE
+ << "' to persist reboot reason";
+ return;
+ }
+ WriteStringToFd(reason, fd);
+ fsync(fd.get());
}
// represents umount status during reboot / shutdown.
@@ -181,7 +189,7 @@
};
// Turn off backlight while we are performing power down cleanup activities.
-static void TurnOffBacklight() REQUIRES(service_lock) {
+static void TurnOffBacklight() {
Service* service = ServiceList::GetInstance().FindService("blank_screen");
if (service == nullptr) {
LOG(WARNING) << "cannot find blank_screen in TurnOffBacklight";
@@ -193,6 +201,7 @@
}
static Result<void> CallVdc(const std::string& system, const std::string& cmd) {
+ LOG(INFO) << "Calling /system/bin/vdc " << system << " " << cmd;
const char* vdc_argv[] = {"/system/bin/vdc", system.c_str(), cmd.c_str()};
int status;
if (logwrap_fork_execvp(arraysize(vdc_argv), vdc_argv, &status, false, LOG_KLOG, true,
@@ -315,9 +324,9 @@
bool* reboot_monitor_run) {
unsigned int remaining_shutdown_time = 0;
- // 30 seconds more than the timeout passed to the thread as there is a final Umount pass
+ // 300 seconds more than the timeout passed to the thread as there is a final Umount pass
// after the timeout is reached.
- constexpr unsigned int shutdown_watchdog_timeout_default = 30;
+ constexpr unsigned int shutdown_watchdog_timeout_default = 300;
auto shutdown_watchdog_timeout = android::base::GetUintProperty(
"ro.build.shutdown.watchdog.timeout", shutdown_watchdog_timeout_default);
remaining_shutdown_time = shutdown_watchdog_timeout + shutdown_timeout.count() / 1000;
@@ -446,10 +455,14 @@
#define ZRAM_RESET "/sys/block/zram0/reset"
#define ZRAM_BACK_DEV "/sys/block/zram0/backing_dev"
static Result<void> KillZramBackingDevice() {
+ if (access(ZRAM_BACK_DEV, F_OK) != 0 && errno == ENOENT) {
+ LOG(INFO) << "No zram backing device configured";
+ return {};
+ }
std::string backing_dev;
- if (!android::base::ReadFileToString(ZRAM_BACK_DEV, &backing_dev)) return {};
-
- if (!android::base::StartsWith(backing_dev, "/dev/block/loop")) return {};
+ if (!android::base::ReadFileToString(ZRAM_BACK_DEV, &backing_dev)) {
+ return ErrnoError() << "Failed to read " << ZRAM_BACK_DEV;
+ }
// cut the last "\n"
backing_dev.erase(backing_dev.length() - 1);
@@ -468,6 +481,11 @@
<< " failed";
}
+ if (!android::base::StartsWith(backing_dev, "/dev/block/loop")) {
+ LOG(INFO) << backing_dev << " is not a loop device. Exiting early";
+ return {};
+ }
+
// clear loopback device
unique_fd loop(TEMP_FAILURE_RETRY(open(backing_dev.c_str(), O_RDWR | O_CLOEXEC)));
if (loop.get() < 0) {
@@ -537,26 +555,6 @@
Timer t;
LOG(INFO) << "Reboot start, reason: " << reason << ", reboot_target: " << reboot_target;
- // Ensure last reboot reason is reduced to canonical
- // alias reported in bootloader or system boot reason.
- size_t skip = 0;
- std::vector<std::string> reasons = Split(reason, ",");
- if (reasons.size() >= 2 && reasons[0] == "reboot" &&
- (reasons[1] == "recovery" || reasons[1] == "bootloader" || reasons[1] == "cold" ||
- reasons[1] == "hard" || reasons[1] == "warm")) {
- skip = strlen("reboot,");
- }
- PersistRebootReason(reason.c_str() + skip, true);
- sync();
-
- // If /data isn't mounted then we can skip the extra reboot steps below, since we don't need to
- // worry about unmounting it.
- if (!IsDataMounted()) {
- sync();
- RebootSystem(cmd, reboot_target);
- abort();
- }
-
bool is_thermal_shutdown = cmd == ANDROID_RB_THERMOFF;
auto shutdown_timeout = 0ms;
@@ -589,7 +587,25 @@
// Start reboot monitor thread
sem_post(&reboot_semaphore);
- auto lock = std::lock_guard{service_lock};
+ // Ensure last reboot reason is reduced to canonical
+ // alias reported in bootloader or system boot reason.
+ size_t skip = 0;
+ std::vector<std::string> reasons = Split(reason, ",");
+ if (reasons.size() >= 2 && reasons[0] == "reboot" &&
+ (reasons[1] == "recovery" || reasons[1] == "bootloader" || reasons[1] == "cold" ||
+ reasons[1] == "hard" || reasons[1] == "warm")) {
+ skip = strlen("reboot,");
+ }
+ PersistRebootReason(reason.c_str() + skip, true);
+
+ // If /data isn't mounted then we can skip the extra reboot steps below, since we don't need to
+ // worry about unmounting it.
+ if (!IsDataMounted()) {
+ sync();
+ RebootSystem(cmd, reboot_target);
+ abort();
+ }
+
// watchdogd is a vendor specific component but should be alive to complete shutdown safely.
const std::set<std::string> to_starts{"watchdogd"};
std::vector<Service*> stop_first;
@@ -709,21 +725,15 @@
// Skip wait for prop if it is in progress
ResetWaitForProp();
// Clear EXEC flag if there is one pending
- auto lock = std::lock_guard{service_lock};
for (const auto& s : ServiceList::GetInstance()) {
s->UnSetExec();
}
- // We no longer process messages about properties changing coming from property service, so we
- // need to tell property service to stop sending us these messages, otherwise it'll fill the
- // buffers and block indefinitely, causing future property sets, including those that init makes
- // during shutdown in Service::NotifyStateChange() to also block indefinitely.
- SendStopSendingMessagesMessage();
}
static void LeaveShutdown() {
LOG(INFO) << "Leaving shutdown mode";
shutting_down = false;
- SendStartSendingMessagesMessage();
+ StartSendingMessages();
}
static Result<void> UnmountAllApexes() {
@@ -738,23 +748,32 @@
return Error() << "'/system/bin/apexd --unmount-all' failed : " << status;
}
+static std::chrono::milliseconds GetMillisProperty(const std::string& name,
+ std::chrono::milliseconds default_value) {
+ auto value = GetUintProperty(name, static_cast<uint64_t>(default_value.count()));
+ return std::chrono::milliseconds(std::move(value));
+}
+
static Result<void> DoUserspaceReboot() {
LOG(INFO) << "Userspace reboot initiated";
- auto guard = android::base::make_scope_guard([] {
+ // An ugly way to pass a more precise reason on why fallback to hard reboot was triggered.
+ std::string sub_reason = "";
+ auto guard = android::base::make_scope_guard([&sub_reason] {
// Leave shutdown so that we can handle a full reboot.
LeaveShutdown();
- trigger_shutdown("reboot,userspace_failed,shutdown_aborted");
+ trigger_shutdown("reboot,userspace_failed,shutdown_aborted," + sub_reason);
});
// Triggering userspace-reboot-requested will result in a bunch of setprop
// actions. We should make sure, that all of them are propagated before
// proceeding with userspace reboot. Synchronously setting sys.init.userspace_reboot.in_progress
// property is not perfect, but it should do the trick.
if (!android::sysprop::InitProperties::userspace_reboot_in_progress(true)) {
+ sub_reason = "setprop";
return Error() << "Failed to set sys.init.userspace_reboot.in_progress property";
}
EnterShutdown();
- auto lock = std::lock_guard{service_lock};
if (!SetProperty("sys.powerctl", "")) {
+ sub_reason = "resetprop";
return Error() << "Failed to reset sys.powerctl property";
}
std::vector<Service*> stop_first;
@@ -776,22 +795,29 @@
sync();
LOG(INFO) << "sync() took " << sync_timer;
}
- // TODO(b/135984674): do we need shutdown animation for userspace reboot?
- // TODO(b/135984674): control userspace timeout via read-only property?
- StopServicesAndLogViolations(stop_first, 10s, true /* SIGTERM */);
- if (int r = StopServicesAndLogViolations(stop_first, 20s, false /* SIGKILL */); r > 0) {
+ auto sigterm_timeout = GetMillisProperty("init.userspace_reboot.sigterm.timeoutmillis", 5s);
+ auto sigkill_timeout = GetMillisProperty("init.userspace_reboot.sigkill.timeoutmillis", 10s);
+ LOG(INFO) << "Timeout to terminate services: " << sigterm_timeout.count() << "ms "
+ << "Timeout to kill services: " << sigkill_timeout.count() << "ms";
+ StopServicesAndLogViolations(stop_first, sigterm_timeout, true /* SIGTERM */);
+ if (int r = StopServicesAndLogViolations(stop_first, sigkill_timeout, false /* SIGKILL */);
+ r > 0) {
+ sub_reason = "sigkill";
// TODO(b/135984674): store information about offending services for debugging.
return Error() << r << " post-data services are still running";
}
if (auto result = KillZramBackingDevice(); !result.ok()) {
+ sub_reason = "zram";
return result;
}
if (auto result = CallVdc("volume", "reset"); !result.ok()) {
+ sub_reason = "vold_reset";
return result;
}
- if (int r = StopServicesAndLogViolations(GetDebuggingServices(true /* only_post_data */), 5s,
- false /* SIGKILL */);
+ if (int r = StopServicesAndLogViolations(GetDebuggingServices(true /* only_post_data */),
+ sigkill_timeout, false /* SIGKILL */);
r > 0) {
+ sub_reason = "sigkill_debug";
// TODO(b/135984674): store information about offending services for debugging.
return Error() << r << " debugging services are still running";
}
@@ -802,9 +828,11 @@
LOG(INFO) << "sync() took " << sync_timer;
}
if (auto result = UnmountAllApexes(); !result.ok()) {
+ sub_reason = "apex";
return result;
}
if (!SwitchToBootstrapMountNamespaceIfNeeded()) {
+ sub_reason = "ns_switch";
return Error() << "Failed to switch to bootstrap namespace";
}
// Remove services that were defined in an APEX.
@@ -828,20 +856,25 @@
}
static void UserspaceRebootWatchdogThread() {
- if (!WaitForProperty("sys.init.userspace_reboot.in_progress", "1", 20s)) {
- // TODO(b/135984674): should we reboot instead?
- LOG(WARNING) << "Userspace reboot didn't start in 20 seconds. Stopping watchdog";
- return;
+ auto started_timeout = GetMillisProperty("init.userspace_reboot.started.timeoutmillis", 10s);
+ if (!WaitForProperty("sys.init.userspace_reboot.in_progress", "1", started_timeout)) {
+ LOG(ERROR) << "Userspace reboot didn't start in " << started_timeout.count()
+ << "ms. Switching to full reboot";
+ // Init might be wedged, don't try to write reboot reason into a persistent property and do
+ // a dirty reboot.
+ PersistRebootReason("userspace_failed,watchdog_triggered,failed_to_start", false);
+ RebootSystem(ANDROID_RB_RESTART2, "userspace_failed,watchdog_triggered,failed_to_start");
}
LOG(INFO) << "Starting userspace reboot watchdog";
- // TODO(b/135984674): this should be configured via a read-only sysprop.
- std::chrono::milliseconds timeout = 60s;
- if (!WaitForProperty("sys.boot_completed", "1", timeout)) {
- LOG(ERROR) << "Failed to boot in " << timeout.count() << "ms. Switching to full reboot";
+ auto watchdog_timeout = GetMillisProperty("init.userspace_reboot.watchdog.timeoutmillis", 5min);
+ LOG(INFO) << "UserspaceRebootWatchdog timeout: " << watchdog_timeout.count() << "ms";
+ if (!WaitForProperty("sys.boot_completed", "1", watchdog_timeout)) {
+ LOG(ERROR) << "Failed to boot in " << watchdog_timeout.count()
+ << "ms. Switching to full reboot";
// In this case device is in a boot loop. Only way to recover is to do dirty reboot.
// Since init might be wedged, don't try to write reboot reason into a persistent property.
- PersistRebootReason("userspace_failed,watchdog_triggered", false);
- RebootSystem(ANDROID_RB_RESTART2, "userspace_failed,watchdog_triggered");
+ PersistRebootReason("userspace_failed,watchdog_triggered,failed_to_boot", false);
+ RebootSystem(ANDROID_RB_RESTART2, "userspace_failed,watchdog_triggered,failed_to_boot");
}
LOG(INFO) << "Device booted, stopping userspace reboot watchdog";
}
@@ -914,7 +947,6 @@
run_fsck = true;
} else if (cmd_params[1] == "thermal") {
// Turn off sources of heat immediately.
- auto lock = std::lock_guard{service_lock};
TurnOffBacklight();
// run_fsck is false to avoid delay
cmd = ANDROID_RB_THERMOFF;
@@ -985,6 +1017,10 @@
return;
}
+ // We do not want to process any messages (queue'ing triggers, shutdown messages, control
+ // messages, etc) from properties during reboot.
+ StopSendingMessages();
+
if (userspace_reboot) {
HandleUserspaceReboot();
return;
diff --git a/init/reboot_utils.cpp b/init/reboot_utils.cpp
index 485188b..76460a5 100644
--- a/init/reboot_utils.cpp
+++ b/init/reboot_utils.cpp
@@ -29,6 +29,7 @@
#include <cutils/android_reboot.h>
#include "capabilities.h"
+#include "reboot_utils.h"
namespace android {
namespace init {
@@ -138,6 +139,9 @@
LOG(ERROR) << backtrace->FormatFrameData(i);
}
if (init_fatal_panic) {
+ LOG(ERROR) << __FUNCTION__ << ": Trigger crash";
+ android::base::WriteStringToFile("c", PROC_SYSRQ);
+ LOG(ERROR) << __FUNCTION__ << ": Sys-Rq failed to crash the system; fallback to exit().";
_exit(signal_number);
}
RebootSystem(ANDROID_RB_RESTART2, init_fatal_reboot_target);
diff --git a/init/reboot_utils.h b/init/reboot_utils.h
index 878ad96..05bb9ae 100644
--- a/init/reboot_utils.h
+++ b/init/reboot_utils.h
@@ -18,6 +18,8 @@
#include <string>
+#define PROC_SYSRQ "/proc/sysrq-trigger"
+
namespace android {
namespace init {
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 2faa167..5a0255a 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -63,11 +63,15 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
+#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <fs_avb/fs_avb.h>
+#include <fs_mgr.h>
#include <libgsi/libgsi.h>
+#include <libsnapshot/snapshot.h>
#include <selinux/android.h>
+#include "block_dev_initializer.h"
#include "debug_ramdisk.h"
#include "reboot_utils.h"
#include "util.h"
@@ -78,6 +82,7 @@
using android::base::Timer;
using android::base::unique_fd;
using android::fs_mgr::AvbHandle;
+using android::snapshot::SnapshotManager;
namespace android {
namespace init {
@@ -537,7 +542,9 @@
// adb remount, snapshot-based updates, and DSUs all create files during
// first-stage init.
- selinux_android_restorecon("/metadata", SELINUX_ANDROID_RESTORECON_RECURSE);
+ selinux_android_restorecon(SnapshotManager::GetGlobalRollbackIndicatorPath().c_str(), 0);
+ selinux_android_restorecon("/metadata/gsi", SELINUX_ANDROID_RESTORECON_RECURSE |
+ SELINUX_ANDROID_RESTORECON_SKIP_SEHASH);
}
int SelinuxKlogCallback(int type, const char* fmt, ...) {
@@ -594,6 +601,74 @@
return vendor_android_version;
}
+// This is for R system.img/system_ext.img to work on old vendor.img as system_ext.img
+// is introduced in R. We mount system_ext in second stage init because the first-stage
+// init in boot.img won't be updated in the system-only OTA scenario.
+void MountMissingSystemPartitions() {
+ android::fs_mgr::Fstab fstab;
+ if (!ReadDefaultFstab(&fstab)) {
+ LOG(ERROR) << "Could not read default fstab";
+ }
+
+ android::fs_mgr::Fstab mounts;
+ if (!ReadFstabFromFile("/proc/mounts", &mounts)) {
+ LOG(ERROR) << "Could not read /proc/mounts";
+ }
+
+ static const std::vector<std::string> kPartitionNames = {"system_ext", "product"};
+
+ android::fs_mgr::Fstab extra_fstab;
+ for (const auto& name : kPartitionNames) {
+ if (GetEntryForMountPoint(&mounts, "/"s + name)) {
+ // The partition is already mounted.
+ continue;
+ }
+
+ auto system_entry = GetEntryForMountPoint(&fstab, "/system");
+ if (!system_entry) {
+ LOG(ERROR) << "Could not find mount entry for /system";
+ break;
+ }
+ if (!system_entry->fs_mgr_flags.logical) {
+ LOG(INFO) << "Skipping mount of " << name << ", system is not dynamic.";
+ break;
+ }
+
+ auto entry = *system_entry;
+ auto partition_name = name + fs_mgr_get_slot_suffix();
+ auto replace_name = "system"s + fs_mgr_get_slot_suffix();
+
+ entry.mount_point = "/"s + name;
+ entry.blk_device =
+ android::base::StringReplace(entry.blk_device, replace_name, partition_name, false);
+ if (!fs_mgr_update_logical_partition(&entry)) {
+ LOG(ERROR) << "Could not update logical partition";
+ continue;
+ }
+
+ extra_fstab.emplace_back(std::move(entry));
+ }
+
+ SkipMountingPartitions(&extra_fstab);
+ if (extra_fstab.empty()) {
+ return;
+ }
+
+ BlockDevInitializer block_dev_init;
+ for (auto& entry : extra_fstab) {
+ if (access(entry.blk_device.c_str(), F_OK) != 0) {
+ auto block_dev = android::base::Basename(entry.blk_device);
+ if (!block_dev_init.InitDmDevice(block_dev)) {
+ LOG(ERROR) << "Failed to find device-mapper node: " << block_dev;
+ continue;
+ }
+ }
+ if (fs_mgr_do_mount_one(entry)) {
+ LOG(ERROR) << "Could not mount " << entry.mount_point;
+ }
+ }
+}
+
int SetupSelinux(char** argv) {
SetStdioToDevNull(argv);
InitKernelLogging(argv);
@@ -604,6 +679,8 @@
boot_clock::time_point start_time = boot_clock::now();
+ MountMissingSystemPartitions();
+
// Set up SELinux, loading the SELinux policy.
SelinuxSetupKernelLogging();
SelinuxInitialize();
diff --git a/init/service.cpp b/init/service.cpp
index b12d11a..165b848 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -90,7 +90,9 @@
<< "\") has incorrect label or no domain transition from " << mycon.get()
<< " to another SELinux domain defined. Have you configured your "
"service correctly? https://source.android.com/security/selinux/"
- "device-policy#label_new_services_and_address_denials";
+ "device-policy#label_new_services_and_address_denials. Note: this "
+ "error shows up even in permissive mode in order to make auditing "
+ "denials possible.";
}
if (rc < 0) {
return Error() << "Could not get process context";
@@ -511,6 +513,10 @@
LOG(ERROR) << "failed to write pid to files: " << result.error();
}
+ if (task_profiles_.size() > 0 && !SetTaskProfiles(getpid(), task_profiles_)) {
+ LOG(ERROR) << "failed to set task profiles";
+ }
+
// As requested, set our gid, supplemental gids, uid, context, and
// priority. Aborts on failure.
SetProcessAttributesAndCaps();
diff --git a/init/service.h b/init/service.h
index d2a4462..34ed5ef 100644
--- a/init/service.h
+++ b/init/service.h
@@ -27,14 +27,12 @@
#include <vector>
#include <android-base/chrono_utils.h>
-#include <android-base/thread_annotations.h>
#include <cutils/iosched_policy.h>
#include "action.h"
#include "capabilities.h"
#include "keyword_map.h"
#include "parser.h"
-#include "service_lock.h"
#include "service_utils.h"
#include "subcontext.h"
@@ -79,17 +77,17 @@
bool IsRunning() { return (flags_ & SVC_RUNNING) != 0; }
bool IsEnabled() { return (flags_ & SVC_DISABLED) == 0; }
- Result<void> ExecStart() REQUIRES(service_lock);
- Result<void> Start() REQUIRES(service_lock);
- Result<void> StartIfNotDisabled() REQUIRES(service_lock);
- Result<void> StartIfPostData() REQUIRES(service_lock);
- Result<void> Enable() REQUIRES(service_lock);
+ Result<void> ExecStart();
+ Result<void> Start();
+ Result<void> StartIfNotDisabled();
+ Result<void> StartIfPostData();
+ Result<void> Enable();
void Reset();
void ResetIfPostData();
void Stop();
void Terminate();
void Timeout();
- void Restart() REQUIRES(service_lock);
+ void Restart();
void Reap(const siginfo_t& siginfo);
void DumpState() const;
void SetShutdownCritical() { flags_ |= SVC_SHUTDOWN_CRITICAL; }
@@ -132,6 +130,13 @@
bool is_updatable() const { return updatable_; }
bool is_post_data() const { return post_data_; }
bool is_from_apex() const { return from_apex_; }
+ void set_oneshot(bool value) {
+ if (value) {
+ flags_ |= SVC_ONESHOT;
+ } else {
+ flags_ &= ~SVC_ONESHOT;
+ }
+ }
private:
void NotifyStateChange(const std::string& new_state) const;
@@ -165,6 +170,8 @@
std::vector<std::string> writepid_files_;
+ std::vector<std::string> task_profiles_;
+
std::set<std::string> interfaces_; // e.g. some.package.foo@1.0::IBaz/instance-name
// keycodes for triggering this service via /dev/input/input*
diff --git a/init/service_list.h b/init/service_list.h
index 280a228..3b9018b 100644
--- a/init/service_list.h
+++ b/init/service_list.h
@@ -17,13 +17,9 @@
#pragma once
#include <memory>
-#include <mutex>
#include <vector>
-#include <android-base/thread_annotations.h>
-
#include "service.h"
-#include "service_lock.h"
namespace android {
namespace init {
@@ -36,16 +32,16 @@
ServiceList();
size_t CheckAllCommands();
- void AddService(std::unique_ptr<Service> service) REQUIRES(service_lock);
- void RemoveService(const Service& svc) REQUIRES(service_lock);
+ void AddService(std::unique_ptr<Service> service);
+ void RemoveService(const Service& svc);
template <class UnaryPredicate>
- void RemoveServiceIf(UnaryPredicate predicate) REQUIRES(service_lock) {
+ void RemoveServiceIf(UnaryPredicate predicate) {
services_.erase(std::remove_if(services_.begin(), services_.end(), predicate),
services_.end());
}
template <typename T, typename F = decltype(&Service::name)>
- Service* FindService(T value, F function = &Service::name) const REQUIRES(service_lock) {
+ Service* FindService(T value, F function = &Service::name) const {
auto svc = std::find_if(services_.begin(), services_.end(),
[&function, &value](const std::unique_ptr<Service>& s) {
return std::invoke(function, s) == value;
@@ -56,7 +52,7 @@
return nullptr;
}
- Service* FindInterface(const std::string& interface_name) REQUIRES(service_lock) {
+ Service* FindInterface(const std::string& interface_name) {
for (const auto& svc : services_) {
if (svc->interfaces().count(interface_name) > 0) {
return svc.get();
@@ -66,20 +62,18 @@
return nullptr;
}
- void DumpState() const REQUIRES(service_lock);
+ void DumpState() const;
- auto begin() const REQUIRES(service_lock) { return services_.begin(); }
- auto end() const REQUIRES(service_lock) { return services_.end(); }
- const std::vector<std::unique_ptr<Service>>& services() const REQUIRES(service_lock) {
- return services_;
- }
- const std::vector<Service*> services_in_shutdown_order() const REQUIRES(service_lock);
+ auto begin() const { return services_.begin(); }
+ auto end() const { return services_.end(); }
+ const std::vector<std::unique_ptr<Service>>& services() const { return services_; }
+ const std::vector<Service*> services_in_shutdown_order() const;
void MarkPostData();
bool IsPostData();
- void MarkServicesUpdate() REQUIRES(service_lock);
+ void MarkServicesUpdate();
bool IsServicesUpdated() const { return services_update_finished_; }
- void DelayService(const Service& service) REQUIRES(service_lock);
+ void DelayService(const Service& service);
void ResetState() {
post_data_ = false;
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index 51f4c97..bdac077 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -168,7 +168,6 @@
const std::string fullname = interface_name + "/" + instance_name;
- auto lock = std::lock_guard{service_lock};
for (const auto& svc : *service_list_) {
if (svc->interfaces().count(fullname) > 0) {
return Error() << "Interface '" << fullname << "' redefined in " << service_->name()
@@ -361,6 +360,12 @@
return Error() << "Invalid shutdown option";
}
+Result<void> ServiceParser::ParseTaskProfiles(std::vector<std::string>&& args) {
+ args.erase(args.begin());
+ service_->task_profiles_ = std::move(args);
+ return {};
+}
+
Result<void> ServiceParser::ParseTimeoutPeriod(std::vector<std::string>&& args) {
int period;
if (!ParseInt(args[1], &period, 1)) {
@@ -530,6 +535,7 @@
{"sigstop", {0, 0, &ServiceParser::ParseSigstop}},
{"socket", {3, 6, &ServiceParser::ParseSocket}},
{"stdio_to_kmsg", {0, 0, &ServiceParser::ParseStdioToKmsg}},
+ {"task_profiles", {1, kMax, &ServiceParser::ParseTaskProfiles}},
{"timeout_period", {1, 1, &ServiceParser::ParseTimeoutPeriod}},
{"updatable", {0, 0, &ServiceParser::ParseUpdatable}},
{"user", {1, 1, &ServiceParser::ParseUser}},
@@ -599,7 +605,13 @@
}
}
- auto lock = std::lock_guard{service_lock};
+ if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_R__) {
+ if ((service_->flags() & SVC_CRITICAL) != 0 && (service_->flags() & SVC_ONESHOT) != 0) {
+ return Error() << "service '" << service_->name()
+ << "' can't be both critical and oneshot";
+ }
+ }
+
Service* old_service = service_list_->FindService(service_->name());
if (old_service) {
if (!service_->is_override()) {
diff --git a/init/service_parser.h b/init/service_parser.h
index 7bb0cc0..0fd2da5 100644
--- a/init/service_parser.h
+++ b/init/service_parser.h
@@ -78,6 +78,7 @@
Result<void> ParseSigstop(std::vector<std::string>&& args);
Result<void> ParseSocket(std::vector<std::string>&& args);
Result<void> ParseStdioToKmsg(std::vector<std::string>&& args);
+ Result<void> ParseTaskProfiles(std::vector<std::string>&& args);
Result<void> ParseTimeoutPeriod(std::vector<std::string>&& args);
Result<void> ParseFile(std::vector<std::string>&& args);
Result<void> ParseUser(std::vector<std::string>&& args);
diff --git a/init/sigchld_handler.cpp b/init/sigchld_handler.cpp
index 064d64d..9b2c7d9 100644
--- a/init/sigchld_handler.cpp
+++ b/init/sigchld_handler.cpp
@@ -64,8 +64,6 @@
std::string wait_string;
Service* service = nullptr;
- auto lock = std::lock_guard{service_lock};
-
if (SubcontextChildReap(pid)) {
name = "Subcontext";
} else {
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/init/sysprop/InitProperties.sysprop b/init/sysprop/InitProperties.sysprop
index b876dc0..24c2434 100644
--- a/init/sysprop/InitProperties.sysprop
+++ b/init/sysprop/InitProperties.sysprop
@@ -31,6 +31,6 @@
type: Boolean
scope: Public
access: Readonly
- prop_name: "ro.init.userspace_reboot.is_supported"
+ prop_name: "init.userspace_reboot.is_supported"
integer_as_bool: true
}
diff --git a/init/sysprop/api/com.android.sysprop.init-current.txt b/init/sysprop/api/com.android.sysprop.init-current.txt
index b8bcef9..01f4e9a 100644
--- a/init/sysprop/api/com.android.sysprop.init-current.txt
+++ b/init/sysprop/api/com.android.sysprop.init-current.txt
@@ -2,7 +2,7 @@
module: "android.sysprop.InitProperties"
prop {
api_name: "is_userspace_reboot_supported"
- prop_name: "ro.init.userspace_reboot.is_supported"
+ prop_name: "init.userspace_reboot.is_supported"
integer_as_bool: true
}
prop {
diff --git a/init/test_kill_services/Android.bp b/init/test_kill_services/Android.bp
new file mode 100644
index 0000000..d59e548
--- /dev/null
+++ b/init/test_kill_services/Android.bp
@@ -0,0 +1,11 @@
+cc_test {
+ name: "init_kill_services_test",
+ srcs: ["init_kill_services_test.cpp"],
+ shared_libs: ["libbase"],
+ test_suites: ["general-tests"],
+
+ // TODO(b/153565474): switch back to auto-generation
+ // and add back:
+ // require_root: true,
+ auto_gen_config: false,
+}
diff --git a/init/test_kill_services/AndroidTest.xml b/init/test_kill_services/AndroidTest.xml
new file mode 100644
index 0000000..8018efa
--- /dev/null
+++ b/init/test_kill_services/AndroidTest.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Runs init_kill_services_test.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+
+ <!-- cannot be autogenerated: b/153565474 -->
+ <target_preparer class="com.android.tradefed.targetprep.RebootTargetPreparer">
+ <!-- flake mitigation, in case device is in bad state-->
+ <option name="pre-reboot" value="true" />
+ <!-- sometimes device gets into bad state, and we don't detect it in this test,
+ so the test succeeds and the next test fails. This is a really bad result, so
+ to avoid that, making sure we reboot the device again before running any more
+ tests.
+ TODO(b/152556737): add metrics for successful device recovery -->
+ <option name="post-reboot" value="true" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="init_kill_services_test->/data/local/tmp/init_kill_services_test" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="init_kill_services_test" />
+ </test>
+</configuration>
diff --git a/init/test_kill_services/init_kill_services_test.cpp b/init/test_kill_services/init_kill_services_test.cpp
new file mode 100644
index 0000000..7e543f2
--- /dev/null
+++ b/init/test_kill_services/init_kill_services_test.cpp
@@ -0,0 +1,69 @@
+/*
+ * 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 <gtest/gtest.h>
+
+#include <android-base/properties.h>
+
+#include <iostream>
+
+using ::android::base::GetProperty;
+using ::android::base::SetProperty;
+
+void ExpectKillingServiceRecovers(const std::string& service_name) {
+ const std::string status_prop = "init.svc." + service_name;
+ const std::string pid_prop = "init.svc_debug_pid." + service_name;
+
+ const std::string initial_pid = GetProperty(pid_prop, "");
+
+ EXPECT_EQ("running", GetProperty(status_prop, "")) << status_prop;
+ EXPECT_NE("", initial_pid) << pid_prop;
+
+ EXPECT_EQ(0, system(("kill -9 " + initial_pid).c_str()));
+
+ constexpr size_t kMaxWaitMilliseconds = 10000;
+ constexpr size_t kRetryWaitMilliseconds = 100;
+
+ constexpr size_t kRetryTimes = kMaxWaitMilliseconds / kRetryWaitMilliseconds;
+
+ for (size_t retry = 0; retry < kRetryTimes; retry++) {
+ const std::string& pid = GetProperty(pid_prop, "");
+ if (pid != initial_pid && pid != "") break;
+ usleep(kRetryWaitMilliseconds * 1000);
+ }
+
+ // svc_debug_pid is set after svc property
+ EXPECT_EQ("running", GetProperty(status_prop, ""));
+}
+
+class InitKillServicesTest : public ::testing::TestWithParam<std::string> {};
+
+TEST_P(InitKillServicesTest, KillCriticalProcesses) {
+ ExpectKillingServiceRecovers(GetParam());
+
+ // sanity check init is still responding
+ EXPECT_TRUE(SetProperty("test.death.test", "asdf"));
+ EXPECT_EQ(GetProperty("test.death.test", ""), "asdf");
+ EXPECT_TRUE(SetProperty("test.death.test", ""));
+}
+
+static inline std::string PrintName(const testing::TestParamInfo<std::string>& info) {
+ return info.param;
+}
+
+INSTANTIATE_TEST_CASE_P(DeathTest, InitKillServicesTest,
+ ::testing::Values("lmkd", "ueventd", "hwservicemanager", "servicemanager"),
+ PrintName);
diff --git a/init/tokenizer_test.cpp b/init/tokenizer_test.cpp
index 6b31683..0122884 100644
--- a/init/tokenizer_test.cpp
+++ b/init/tokenizer_test.cpp
@@ -28,7 +28,7 @@
void RunTest(const std::string& data, const std::vector<std::vector<std::string>>& expected_tokens) {
auto data_copy = std::string{data};
- data_copy.push_back('\n'); // TODO: fix tokenizer
+ data_copy.push_back('\n');
data_copy.push_back('\0');
parse_state state;
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index d2b503b..7514b61 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -285,7 +285,6 @@
// Keep the current product name base configuration so we remain backwards compatible and
// allow it to override everything.
- // TODO: cleanup platform ueventd.rc to remove vendor specific device node entries (b/34968103)
auto hardware = android::base::GetProperty("ro.hardware", "");
auto ueventd_configuration = ParseConfig({"/system/etc/ueventd.rc", "/vendor/ueventd.rc",
diff --git a/init/ueventd_test.cpp b/init/ueventd_test.cpp
index 2d7d2f8..fc3cdfb 100644
--- a/init/ueventd_test.cpp
+++ b/init/ueventd_test.cpp
@@ -53,11 +53,7 @@
};
std::vector<std::thread> threads;
- // TODO(b/63712782): Structured bindings + templated containers are broken in clang :(
- // for (const auto& [file, parameter] : files_and_parameters) {
- for (const auto& pair : files_and_parameters) {
- const auto& file = pair.first;
- const auto& parameter = pair.second;
+ for (const auto& [file, parameter] : files_and_parameters) {
threads.emplace_back(std::thread(make_thread_function(file, parameter)));
}
diff --git a/init/util.cpp b/init/util.cpp
index 24f94ec..40f24b1 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -30,7 +30,9 @@
#include <time.h>
#include <unistd.h>
+#include <mutex>
#include <thread>
+#include <vector>
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -570,6 +572,48 @@
return MkdirOptions{args[1], mode, *uid, *gid, fscrypt_action, ref_option};
}
+Result<MountAllOptions> ParseMountAll(const std::vector<std::string>& args) {
+ bool compat_mode = false;
+ bool import_rc = false;
+ if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_Q__) {
+ if (args.size() <= 1) {
+ return Error() << "mount_all requires at least 1 argument";
+ }
+ compat_mode = true;
+ import_rc = true;
+ }
+
+ std::size_t first_option_arg = args.size();
+ enum mount_mode mode = MOUNT_MODE_DEFAULT;
+
+ // If we are <= Q, then stop looking for non-fstab arguments at slot 2.
+ // Otherwise, stop looking at slot 1 (as the fstab path argument is optional >= R).
+ for (std::size_t na = args.size() - 1; na > (compat_mode ? 1 : 0); --na) {
+ if (args[na] == "--early") {
+ first_option_arg = na;
+ mode = MOUNT_MODE_EARLY;
+ } else if (args[na] == "--late") {
+ first_option_arg = na;
+ mode = MOUNT_MODE_LATE;
+ import_rc = false;
+ }
+ }
+
+ std::string fstab_path;
+ if (first_option_arg > 1) {
+ fstab_path = args[1];
+ } else if (compat_mode) {
+ return Error() << "mount_all argument 1 must be the fstab path";
+ }
+
+ std::vector<std::string> rc_paths;
+ for (std::size_t na = 2; na < first_option_arg; ++na) {
+ rc_paths.push_back(args[na]);
+ }
+
+ return MountAllOptions{rc_paths, fstab_path, mode, import_rc};
+}
+
Result<std::pair<int, std::vector<std::string>>> ParseRestorecon(
const std::vector<std::string>& args) {
struct flag_type {
@@ -610,6 +654,15 @@
return std::pair(flag, paths);
}
+Result<std::string> ParseUmountAll(const std::vector<std::string>& args) {
+ if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_Q__) {
+ if (args.size() <= 1) {
+ return Error() << "umount_all requires at least 1 argument";
+ }
+ }
+ return args[1];
+}
+
static void InitAborter(const char* abort_message) {
// When init forks, it continues to use this aborter for LOG(FATAL), but we want children to
// simply abort instead of trying to reboot the system.
@@ -660,5 +713,50 @@
return access("/system/bin/recovery", F_OK) == 0;
}
+// TODO(b/155203339): remove this
+// Devices in the lab seem to be stuck during shutdown, but the logs don't capture the last actions
+// before shutdown started, so we record those lines, ignoring requests to shutdown, and replay them
+// if we identify that the device is stuck.
+constexpr size_t kRecordedLogsSize = 30;
+std::string recorded_logs[kRecordedLogsSize];
+size_t recorded_log_position = 0;
+std::mutex recorded_logs_lock;
+
+void InitSecondStageLogging(char** argv) {
+ SetFatalRebootTarget();
+ auto second_stage_logger = [](android::base::LogId log_id, android::base::LogSeverity severity,
+ const char* tag, const char* file, unsigned int line,
+ const char* message) {
+ // We only store logs for init, not its children, and only if they're not related to
+ // sys.powerctl.
+ if (getpid() == 1 && strstr(message, "sys.powerctl") == nullptr) {
+ auto lock = std::lock_guard{recorded_logs_lock};
+ recorded_logs[recorded_log_position++] = message;
+ if (recorded_log_position == kRecordedLogsSize) {
+ recorded_log_position = 0;
+ }
+ }
+ android::base::KernelLogger(log_id, severity, tag, file, line, message);
+ };
+ android::base::InitLogging(argv, second_stage_logger, InitAborter);
+}
+
+void DumpShutdownDebugInformation() {
+ auto lock = std::lock_guard{recorded_logs_lock};
+ android::base::KernelLogger(
+ android::base::MAIN, android::base::ERROR, "init", nullptr, 0,
+ "===================== Dumping previous init lines =====================");
+ for (size_t i = recorded_log_position; i < kRecordedLogsSize; ++i) {
+ android::base::KernelLogger(android::base::MAIN, android::base::ERROR, "init", nullptr, 0,
+ recorded_logs[i].c_str());
+ }
+ for (size_t i = 0; i < recorded_log_position; ++i) {
+ android::base::KernelLogger(android::base::MAIN, android::base::ERROR, "init", nullptr, 0,
+ recorded_logs[i].c_str());
+ }
+ android::base::KernelLogger(android::base::MAIN, android::base::ERROR, "init", nullptr, 0,
+ "===================== End of dump =====================");
+}
+
} // namespace init
} // namespace android
diff --git a/init/util.h b/init/util.h
index ad322d9..28f6b18 100644
--- a/init/util.h
+++ b/init/util.h
@@ -22,6 +22,7 @@
#include <chrono>
#include <functional>
#include <string>
+#include <vector>
#include <android-base/chrono_utils.h>
@@ -33,6 +34,12 @@
namespace android {
namespace init {
+enum mount_mode {
+ MOUNT_MODE_DEFAULT = 0,
+ MOUNT_MODE_EARLY = 1,
+ MOUNT_MODE_LATE = 2,
+};
+
static const char kColdBootDoneProp[] = "ro.cold_boot_done";
extern void (*trigger_shutdown)(const std::string& command);
@@ -73,11 +80,24 @@
Result<MkdirOptions> ParseMkdir(const std::vector<std::string>& args);
+struct MountAllOptions {
+ std::vector<std::string> rc_paths;
+ std::string fstab_path;
+ mount_mode mode;
+ bool import_rc;
+};
+
+Result<MountAllOptions> ParseMountAll(const std::vector<std::string>& args);
+
Result<std::pair<int, std::vector<std::string>>> ParseRestorecon(
const std::vector<std::string>& args);
+Result<std::string> ParseUmountAll(const std::vector<std::string>& args);
+
void SetStdioToDevNull(char** argv);
void InitKernelLogging(char** argv);
+void InitSecondStageLogging(char** argv);
+void DumpShutdownDebugInformation();
bool IsRecoveryMode();
} // namespace init
} // namespace android
diff --git a/init/util_test.cpp b/init/util_test.cpp
index 96a5b55..565e7d4 100644
--- a/init/util_test.cpp
+++ b/init/util_test.cpp
@@ -61,8 +61,8 @@
TEST(util, ReadFileSymbolicLink) {
errno = 0;
- // lrw------- 1 root root 23 2008-12-31 19:00 default.prop -> system/etc/prop.default
- auto file_contents = ReadFile("/default.prop");
+ // lrwxr-xr-x 1 root shell 6 2009-01-01 09:00 /system/bin/ps -> toybox
+ auto file_contents = ReadFile("/system/bin/ps");
EXPECT_EQ(ELOOP, errno);
ASSERT_FALSE(file_contents.ok());
EXPECT_EQ("open() failed: Too many symbolic links encountered",
diff --git a/libasyncio/Android.bp b/libasyncio/Android.bp
index 4ab439d..44e7933 100644
--- a/libasyncio/Android.bp
+++ b/libasyncio/Android.bp
@@ -28,6 +28,10 @@
defaults: ["libasyncio_defaults"],
vendor_available: true,
recovery_available: true,
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.adbd",
+ ],
host_supported: true,
srcs: [
"AsyncIO.cpp",
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index 565f2c3..c4cfa6f 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -44,18 +44,16 @@
recovery_available: true,
native_bridge_supported: true,
export_include_dirs: ["include"],
+ apex_available: [
+ "//apex_available:platform",
+ "//apex_available:anyapex",
+ ],
+ min_sdk_version: "apex_inherit",
}
-cc_library {
- name: "libbacktrace",
- vendor_available: false,
- recovery_available: true,
- vndk: {
- enabled: true,
- support_system_process: true,
- },
+cc_defaults {
+ name: "libbacktrace_defaults",
defaults: ["libbacktrace_common"],
- host_supported: true,
cflags: [
"-Wexit-time-destructors",
@@ -80,7 +78,6 @@
shared_libs: [
"libbase",
"liblog",
- "libunwindstack",
],
static_libs: [
@@ -89,6 +86,33 @@
},
android: {
static_libs: ["libasync_safe"],
+ static: {
+ whole_static_libs: ["libasync_safe"],
+ },
+ },
+ },
+}
+
+cc_library {
+ name: "libbacktrace",
+ vendor_available: false,
+ recovery_available: true,
+ apex_available: [
+ "//apex_available:platform",
+ "//apex_available:anyapex",
+ ],
+ vndk: {
+ enabled: true,
+ support_system_process: true,
+ },
+ host_supported: true,
+ defaults: ["libbacktrace_defaults"],
+
+ target: {
+ linux: {
+ shared_libs: [
+ "libunwindstack",
+ ],
},
vendor: {
cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
@@ -99,6 +123,21 @@
},
}
+// Static library without DEX support to avoid dependencies on the ART APEX.
+cc_library_static {
+ name: "libbacktrace_no_dex",
+ visibility: ["//system/core/debuggerd"],
+ defaults: ["libbacktrace_defaults"],
+ cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
+ target: {
+ linux: {
+ static_libs: [
+ "libunwindstack_no_dex",
+ ],
+ },
+ },
+}
+
cc_test_library {
name: "libbacktrace_test",
defaults: ["libbacktrace_common"],
diff --git a/libcrypto_utils/Android.bp b/libcrypto_utils/Android.bp
index e47560f..d7175e0 100644
--- a/libcrypto_utils/Android.bp
+++ b/libcrypto_utils/Android.bp
@@ -38,4 +38,8 @@
enabled: true,
},
},
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.adbd",
+ ],
}
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 8e90ddf..60400c9 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -29,6 +29,11 @@
vendor_available: true,
recovery_available: true,
host_supported: true,
+ apex_available: [
+ "//apex_available:platform",
+ "//apex_available:anyapex",
+ ],
+ min_sdk_version: "29",
native_bridge_supported: true,
export_include_dirs: ["include"],
target: {
@@ -45,13 +50,9 @@
}
// Socket specific parts of libcutils that are safe to statically link into an APEX.
-cc_library_static {
+cc_library {
name: "libcutils_sockets",
vendor_available: true,
- vndk: {
- enabled: true,
- support_system_process: true,
- },
recovery_available: true,
host_supported: true,
native_bridge_supported: true,
@@ -59,9 +60,11 @@
"//apex_available:platform",
"//apex_available:anyapex",
],
+ min_sdk_version: "29",
export_include_dirs: ["include"],
+ shared_libs: ["liblog"],
srcs: ["sockets.cpp"],
target: {
linux_bionic: {
@@ -137,6 +140,11 @@
},
recovery_available: true,
host_supported: true,
+ apex_available: [
+ "//apex_available:platform",
+ "//apex_available:anyapex",
+ ],
+ min_sdk_version: "29",
native_bridge_supported: true,
srcs: [
"config_utils.cpp",
@@ -144,6 +152,7 @@
"iosched_policy.cpp",
"load_file.cpp",
"native_handle.cpp",
+ "properties.cpp",
"record_stream.cpp",
"strlcpy.c",
"threads.cpp",
@@ -179,7 +188,6 @@
"fs_config.cpp",
"klog.cpp",
"partition_utils.cpp",
- "properties.cpp",
"qtaguid.cpp",
"trace-dev.cpp",
"uevent.cpp",
@@ -260,6 +268,7 @@
name: "libcutils_test_default",
srcs: [
"native_handle_test.cpp",
+ "properties_test.cpp",
"sockets_test.cpp",
],
@@ -272,7 +281,6 @@
"fs_config_test.cpp",
"memset_test.cpp",
"multiuser_test.cpp",
- "properties_test.cpp",
"sched_policy_test.cpp",
"str_parms_test.cpp",
"trace-dev_test.cpp",
@@ -341,7 +349,7 @@
cc_test {
name: "KernelLibcutilsTest",
- test_suites: ["general-tests", "vts-core"],
+ test_suites: ["general-tests", "vts"],
defaults: ["libcutils_test_static_defaults"],
test_config: "KernelLibcutilsTest.xml",
}
diff --git a/libcutils/arch-x86/android_memset16.S b/libcutils/arch-x86/android_memset16.S
index cb2ff14..f4d497e 100755
--- a/libcutils/arch-x86/android_memset16.S
+++ b/libcutils/arch-x86/android_memset16.S
@@ -105,14 +105,16 @@
/* We loaded the jump table and adjuested EDX. Go. */ \
jmp *%ebx
- .section .gnu.linkonce.t.__x86.get_pc_thunk.bx,"ax",@progbits
+ .section .text.__x86.get_pc_thunk.bx,"axG",@progbits,__x86.get_pc_thunk.bx,comdat
.globl __x86.get_pc_thunk.bx
.hidden __x86.get_pc_thunk.bx
ALIGN (4)
.type __x86.get_pc_thunk.bx,@function
__x86.get_pc_thunk.bx:
+ cfi_startproc
movl (%esp), %ebx
ret
+ cfi_endproc
#else
# define ENTRANCE
# define RETURN_END ret
diff --git a/libcutils/arch-x86/android_memset32.S b/libcutils/arch-x86/android_memset32.S
index f4326dc..b928f6b 100755
--- a/libcutils/arch-x86/android_memset32.S
+++ b/libcutils/arch-x86/android_memset32.S
@@ -105,14 +105,16 @@
/* We loaded the jump table and adjuested EDX. Go. */ \
jmp *%ebx
- .section .gnu.linkonce.t.__x86.get_pc_thunk.bx,"ax",@progbits
+ .section .text.__x86.get_pc_thunk.bx,"axG",@progbits,__x86.get_pc_thunk.bx,comdat
.globl __x86.get_pc_thunk.bx
.hidden __x86.get_pc_thunk.bx
ALIGN (4)
.type __x86.get_pc_thunk.bx,@function
__x86.get_pc_thunk.bx:
+ cfi_startproc
movl (%esp), %ebx
ret
+ cfi_endproc
#else
# define ENTRANCE
# define RETURN_END ret
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index c4e4f85..5805a4d 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -201,6 +201,8 @@
CAP_MASK_LONG(CAP_SETGID),
"system/bin/simpleperf_app_runner" },
{ 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/e2fsck" },
+ { 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/tune2fs" },
+ { 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/resize2fs" },
// generic defaults
{ 00755, AID_ROOT, AID_ROOT, 0, "bin/*" },
{ 00640, AID_ROOT, AID_SHELL, 0, "fstab.*" },
diff --git a/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/libcutils/include/cutils/properties.h b/libcutils/include/cutils/properties.h
index d2e0871..78d8bc6 100644
--- a/libcutils/include/cutils/properties.h
+++ b/libcutils/include/cutils/properties.h
@@ -14,27 +14,30 @@
* limitations under the License.
*/
-#ifndef __CUTILS_PROPERTIES_H
-#define __CUTILS_PROPERTIES_H
+#pragma once
#include <sys/cdefs.h>
#include <stddef.h>
-#include <sys/system_properties.h>
#include <stdint.h>
+#if __has_include(<sys/system_properties.h>)
+#include <sys/system_properties.h>
+#else
+#define PROP_VALUE_MAX 92
+#endif
+
#ifdef __cplusplus
extern "C" {
#endif
-/* System properties are *small* name value pairs managed by the
-** property service. If your data doesn't fit in the provided
-** space it is not appropriate for a system property.
-**
-** WARNING: system/bionic/include/sys/system_properties.h also defines
-** these, but with different names. (TODO: fix that)
-*/
-#define PROPERTY_KEY_MAX PROP_NAME_MAX
-#define PROPERTY_VALUE_MAX PROP_VALUE_MAX
+//
+// Deprecated.
+//
+// See <android-base/properties.h> for better API.
+//
+
+#define PROPERTY_KEY_MAX PROP_NAME_MAX
+#define PROPERTY_VALUE_MAX PROP_VALUE_MAX
/* property_get: returns the length of the value which will never be
** greater than PROPERTY_VALUE_MAX - 1 and will always be zero terminated.
@@ -146,5 +149,3 @@
#ifdef __cplusplus
}
#endif
-
-#endif
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index b73a29b..e4f45a8 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -34,14 +34,7 @@
* partition, from which the system reads passwd and group files.
*/
-#ifndef _ANDROID_FILESYSTEM_CONFIG_H_
-#define _ANDROID_FILESYSTEM_CONFIG_H_
-
-#include <sys/types.h>
-
-#if !defined(__ANDROID_VNDK__) && !defined(EXCLUDE_FS_CONFIG_STRUCTURES)
-#include <private/fs_config.h>
-#endif
+#pragma once
/* This is the master Users and Groups config for the platform.
* DO NOT EVER RENUMBER
@@ -224,5 +217,3 @@
* documented at the top of this header file.
* Also see build/tools/fs_config for more details.
*/
-
-#endif
diff --git a/libcutils/include/private/canned_fs_config.h b/libcutils/include/private/canned_fs_config.h
index 135b91c..ad4de4c 100644
--- a/libcutils/include/private/canned_fs_config.h
+++ b/libcutils/include/private/canned_fs_config.h
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-#ifndef _CANNED_FS_CONFIG_H
-#define _CANNED_FS_CONFIG_H
+#pragma once
#include <inttypes.h>
+#include <sys/cdefs.h>
__BEGIN_DECLS
@@ -26,5 +26,3 @@
unsigned* gid, unsigned* mode, uint64_t* capabilities);
__END_DECLS
-
-#endif
diff --git a/libcutils/properties.cpp b/libcutils/properties.cpp
index 5dbbeba..03f0496 100644
--- a/libcutils/properties.cpp
+++ b/libcutils/properties.cpp
@@ -16,27 +16,19 @@
#include <cutils/properties.h>
-#define LOG_TAG "properties"
-// #define LOG_NDEBUG 0
-
-#include <assert.h>
-#include <ctype.h>
#include <errno.h>
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-#include <cutils/sockets.h>
-#include <log/log.h>
+#include <android-base/properties.h>
-int8_t property_get_bool(const char *key, int8_t default_value) {
- if (!key) {
- return default_value;
- }
+int8_t property_get_bool(const char* key, int8_t default_value) {
+ if (!key) return default_value;
int8_t result = default_value;
- char buf[PROPERTY_VALUE_MAX] = {'\0'};
+ char buf[PROPERTY_VALUE_MAX] = {};
int len = property_get(key, buf, "");
if (len == 1) {
@@ -57,73 +49,53 @@
return result;
}
-// Convert string property to int (default if fails); return default value if out of bounds
-static intmax_t property_get_imax(const char *key, intmax_t lower_bound, intmax_t upper_bound,
- intmax_t default_value) {
- if (!key) {
- return default_value;
+template <typename T>
+static T property_get_int(const char* key, T default_value) {
+ if (!key) return default_value;
+
+ char value[PROPERTY_VALUE_MAX] = {};
+ if (property_get(key, value, "") < 1) return default_value;
+
+ // libcutils unwisely allows octal, which libbase doesn't.
+ T result = default_value;
+ int saved_errno = errno;
+ errno = 0;
+ char* end = nullptr;
+ intmax_t v = strtoimax(value, &end, 0);
+ if (errno != ERANGE && end != value && v >= std::numeric_limits<T>::min() &&
+ v <= std::numeric_limits<T>::max()) {
+ result = v;
}
-
- intmax_t result = default_value;
- char buf[PROPERTY_VALUE_MAX] = {'\0'};
- char *end = NULL;
-
- int len = property_get(key, buf, "");
- if (len > 0) {
- int tmp = errno;
- errno = 0;
-
- // Infer base automatically
- result = strtoimax(buf, &end, /*base*/ 0);
- if ((result == INTMAX_MIN || result == INTMAX_MAX) && errno == ERANGE) {
- // Over or underflow
- result = default_value;
- ALOGV("%s(%s,%" PRIdMAX ") - overflow", __FUNCTION__, key, default_value);
- } else if (result < lower_bound || result > upper_bound) {
- // Out of range of requested bounds
- result = default_value;
- ALOGV("%s(%s,%" PRIdMAX ") - out of range", __FUNCTION__, key, default_value);
- } else if (end == buf) {
- // Numeric conversion failed
- result = default_value;
- ALOGV("%s(%s,%" PRIdMAX ") - numeric conversion failed", __FUNCTION__, key,
- default_value);
- }
-
- errno = tmp;
- }
-
+ errno = saved_errno;
return result;
}
-int64_t property_get_int64(const char *key, int64_t default_value) {
- return (int64_t)property_get_imax(key, INT64_MIN, INT64_MAX, default_value);
+int64_t property_get_int64(const char* key, int64_t default_value) {
+ return property_get_int<int64_t>(key, default_value);
}
-int32_t property_get_int32(const char *key, int32_t default_value) {
- return (int32_t)property_get_imax(key, INT32_MIN, INT32_MAX, default_value);
+int32_t property_get_int32(const char* key, int32_t default_value) {
+ return property_get_int<int32_t>(key, default_value);
}
-#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
-#include <sys/_system_properties.h>
-
-int property_set(const char *key, const char *value) {
+int property_set(const char* key, const char* value) {
return __system_property_set(key, value);
}
-int property_get(const char *key, char *value, const char *default_value) {
+int property_get(const char* key, char* value, const char* default_value) {
int len = __system_property_get(key, value);
- if (len > 0) {
- return len;
- }
- if (default_value) {
- len = strnlen(default_value, PROPERTY_VALUE_MAX - 1);
- memcpy(value, default_value, len);
- value[len] = '\0';
+ if (len < 1 && default_value) {
+ snprintf(value, PROPERTY_VALUE_MAX, "%s", default_value);
+ return strlen(value);
}
return len;
}
+#if __has_include(<sys/system_properties.h>)
+
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+
struct callback_data {
void (*callback)(const char* name, const char* value, void* cookie);
void* cookie;
@@ -139,6 +111,8 @@
}
int property_list(void (*fn)(const char* name, const char* value, void* cookie), void* cookie) {
- callback_data data = { fn, cookie };
+ callback_data data = {fn, cookie};
return __system_property_foreach(property_list_callback, &data);
}
+
+#endif
diff --git a/libcutils/properties_test.cpp b/libcutils/properties_test.cpp
index 7921972..efc0183 100644
--- a/libcutils/properties_test.cpp
+++ b/libcutils/properties_test.cpp
@@ -93,160 +93,179 @@
}
};
-TEST_F(PropertiesTest, SetString) {
-
+TEST_F(PropertiesTest, property_set_null_key) {
// Null key -> unsuccessful set
- {
- // Null key -> fails
- EXPECT_GT(0, property_set(/*key*/NULL, PROPERTY_TEST_VALUE_DEFAULT));
- }
+ EXPECT_GT(0, property_set(/*key*/ NULL, PROPERTY_TEST_VALUE_DEFAULT));
+}
- // Null value -> returns default value
- {
- // Null value -> OK , and it clears the value
- EXPECT_OK(property_set(PROPERTY_TEST_KEY, /*value*/NULL));
- ResetValue();
+TEST_F(PropertiesTest, property_set_null_value) {
+ // Null value -> OK, and it clears the value
+ EXPECT_OK(property_set(PROPERTY_TEST_KEY, /*value*/ NULL));
+ ResetValue();
- // Since the value is null, default value will be returned
- size_t len = property_get(PROPERTY_TEST_KEY, mValue, PROPERTY_TEST_VALUE_DEFAULT);
- EXPECT_EQ(strlen(PROPERTY_TEST_VALUE_DEFAULT), len);
- EXPECT_STREQ(PROPERTY_TEST_VALUE_DEFAULT, mValue);
- }
+ // Since the value is null, default value will be returned
+ size_t len = property_get(PROPERTY_TEST_KEY, mValue, PROPERTY_TEST_VALUE_DEFAULT);
+ EXPECT_EQ(strlen(PROPERTY_TEST_VALUE_DEFAULT), len);
+ EXPECT_STREQ(PROPERTY_TEST_VALUE_DEFAULT, mValue);
+}
+TEST_F(PropertiesTest, property_set) {
// Trivial case => get returns what was set
- {
- size_t len = SetAndGetProperty("hello_world");
- EXPECT_EQ(strlen("hello_world"), len) << "hello_world key";
- EXPECT_STREQ("hello_world", mValue);
- ResetValue();
- }
+ size_t len = SetAndGetProperty("hello_world");
+ EXPECT_EQ(strlen("hello_world"), len) << "hello_world key";
+ EXPECT_STREQ("hello_world", mValue);
+ ResetValue();
+}
+TEST_F(PropertiesTest, property_set_empty) {
// Set to empty string => get returns default always
- {
- const char* EMPTY_STRING_DEFAULT = "EMPTY_STRING";
- size_t len = SetAndGetProperty("", EMPTY_STRING_DEFAULT);
- EXPECT_EQ(strlen(EMPTY_STRING_DEFAULT), len) << "empty key";
- EXPECT_STREQ(EMPTY_STRING_DEFAULT, mValue);
- ResetValue();
- }
+ const char* EMPTY_STRING_DEFAULT = "EMPTY_STRING";
+ size_t len = SetAndGetProperty("", EMPTY_STRING_DEFAULT);
+ EXPECT_EQ(strlen(EMPTY_STRING_DEFAULT), len) << "empty key";
+ EXPECT_STREQ(EMPTY_STRING_DEFAULT, mValue);
+ ResetValue();
+}
+TEST_F(PropertiesTest, property_set_max_length) {
// Set to max length => get returns what was set
- {
- std::string maxLengthString = std::string(PROPERTY_VALUE_MAX-1, 'a');
+ std::string maxLengthString = std::string(PROPERTY_VALUE_MAX - 1, 'a');
- int len = SetAndGetProperty(maxLengthString.c_str());
- EXPECT_EQ(PROPERTY_VALUE_MAX-1, len) << "max length key";
- EXPECT_STREQ(maxLengthString.c_str(), mValue);
- ResetValue();
- }
+ int len = SetAndGetProperty(maxLengthString.c_str());
+ EXPECT_EQ(PROPERTY_VALUE_MAX - 1, len) << "max length key";
+ EXPECT_STREQ(maxLengthString.c_str(), mValue);
+ ResetValue();
+}
+TEST_F(PropertiesTest, property_set_too_long) {
// Set to max length + 1 => set fails
- {
- const char* VALID_TEST_VALUE = "VALID_VALUE";
- ASSERT_OK(property_set(PROPERTY_TEST_KEY, VALID_TEST_VALUE));
+ const char* VALID_TEST_VALUE = "VALID_VALUE";
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, VALID_TEST_VALUE));
- std::string oneLongerString = std::string(PROPERTY_VALUE_MAX, 'a');
+ std::string oneLongerString = std::string(PROPERTY_VALUE_MAX, 'a');
- // Expect that the value set fails since it's too long
- EXPECT_GT(0, property_set(PROPERTY_TEST_KEY, oneLongerString.c_str()));
- size_t len = property_get(PROPERTY_TEST_KEY, mValue, PROPERTY_TEST_VALUE_DEFAULT);
+ // Expect that the value set fails since it's too long
+ EXPECT_GT(0, property_set(PROPERTY_TEST_KEY, oneLongerString.c_str()));
+ size_t len = property_get(PROPERTY_TEST_KEY, mValue, PROPERTY_TEST_VALUE_DEFAULT);
- EXPECT_EQ(strlen(VALID_TEST_VALUE), len) << "set should've failed";
- EXPECT_STREQ(VALID_TEST_VALUE, mValue);
- ResetValue();
- }
+ EXPECT_EQ(strlen(VALID_TEST_VALUE), len) << "set should've failed";
+ EXPECT_STREQ(VALID_TEST_VALUE, mValue);
+ ResetValue();
}
-TEST_F(PropertiesTest, GetString) {
-
+TEST_F(PropertiesTest, property_get_too_long) {
// Try to use a default value that's too long => get truncates the value
- {
- ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
- std::string maxLengthString = std::string(PROPERTY_VALUE_MAX - 1, 'a');
- std::string oneLongerString = std::string(PROPERTY_VALUE_MAX, 'a');
+ std::string maxLengthString = std::string(PROPERTY_VALUE_MAX - 1, 'a');
+ std::string oneLongerString = std::string(PROPERTY_VALUE_MAX, 'a');
- // Expect that the value is truncated since it's too long (by 1)
- int len = property_get(PROPERTY_TEST_KEY, mValue, oneLongerString.c_str());
- EXPECT_EQ(PROPERTY_VALUE_MAX - 1, len);
- EXPECT_STREQ(maxLengthString.c_str(), mValue);
- ResetValue();
- }
-
- // Try to use a default value that's the max length => get succeeds
- {
- ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
-
- std::string maxLengthString = std::string(PROPERTY_VALUE_MAX - 1, 'b');
-
- // Expect that the value matches maxLengthString
- int len = property_get(PROPERTY_TEST_KEY, mValue, maxLengthString.c_str());
- EXPECT_EQ(PROPERTY_VALUE_MAX - 1, len);
- EXPECT_STREQ(maxLengthString.c_str(), mValue);
- ResetValue();
- }
-
- // Try to use a default value of length one => get succeeds
- {
- ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
-
- std::string oneCharString = std::string(1, 'c');
-
- // Expect that the value matches oneCharString
- int len = property_get(PROPERTY_TEST_KEY, mValue, oneCharString.c_str());
- EXPECT_EQ(1, len);
- EXPECT_STREQ(oneCharString.c_str(), mValue);
- ResetValue();
- }
-
- // Try to use a default value of length zero => get succeeds
- {
- ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
-
- std::string zeroCharString = std::string(0, 'd');
-
- // Expect that the value matches oneCharString
- int len = property_get(PROPERTY_TEST_KEY, mValue, zeroCharString.c_str());
- EXPECT_EQ(0, len);
- EXPECT_STREQ(zeroCharString.c_str(), mValue);
- ResetValue();
- }
-
- // Try to use a NULL default value => get returns 0
- {
- ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
-
- // Expect a return value of 0
- int len = property_get(PROPERTY_TEST_KEY, mValue, NULL);
- EXPECT_EQ(0, len);
- ResetValue();
- }
+ // Expect that the value is truncated since it's too long (by 1)
+ int len = property_get(PROPERTY_TEST_KEY, mValue, oneLongerString.c_str());
+ EXPECT_EQ(PROPERTY_VALUE_MAX - 1, len);
+ EXPECT_STREQ(maxLengthString.c_str(), mValue);
+ ResetValue();
}
-TEST_F(PropertiesTest, GetBool) {
- /**
- * TRUE
- */
- const char *valuesTrue[] = { "1", "true", "y", "yes", "on", };
- for (size_t i = 0; i < arraysize(valuesTrue); ++i) {
- ASSERT_OK(property_set(PROPERTY_TEST_KEY, valuesTrue[i]));
- bool val = property_get_bool(PROPERTY_TEST_KEY, /*default_value*/false);
- EXPECT_TRUE(val) << "Property should've been TRUE for value: '" << valuesTrue[i] << "'";
- }
+TEST_F(PropertiesTest, property_get_default_too_long) {
+ // Try to use a default value that's the max length => get succeeds
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
- /**
- * FALSE
- */
- const char *valuesFalse[] = { "0", "false", "n", "no", "off", };
- for (size_t i = 0; i < arraysize(valuesFalse); ++i) {
- ASSERT_OK(property_set(PROPERTY_TEST_KEY, valuesFalse[i]));
- bool val = property_get_bool(PROPERTY_TEST_KEY, /*default_value*/true);
- EXPECT_FALSE(val) << "Property shoud've been FALSE For string value: '" << valuesFalse[i] << "'";
- }
+ std::string maxLengthString = std::string(PROPERTY_VALUE_MAX - 1, 'b');
- /**
- * NEITHER
- */
+ // Expect that the value matches maxLengthString
+ int len = property_get(PROPERTY_TEST_KEY, mValue, maxLengthString.c_str());
+ EXPECT_EQ(PROPERTY_VALUE_MAX - 1, len);
+ EXPECT_STREQ(maxLengthString.c_str(), mValue);
+ ResetValue();
+}
+
+TEST_F(PropertiesTest, property_get_default_okay) {
+ // Try to use a default value of length one => get succeeds
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
+
+ std::string oneCharString = std::string(1, 'c');
+
+ // Expect that the value matches oneCharString
+ int len = property_get(PROPERTY_TEST_KEY, mValue, oneCharString.c_str());
+ EXPECT_EQ(1, len);
+ EXPECT_STREQ(oneCharString.c_str(), mValue);
+ ResetValue();
+}
+
+TEST_F(PropertiesTest, property_get_default_empty) {
+ // Try to use a default value of length zero => get succeeds
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
+
+ std::string zeroCharString = std::string(0, 'd');
+
+ // Expect that the value matches oneCharString
+ int len = property_get(PROPERTY_TEST_KEY, mValue, zeroCharString.c_str());
+ EXPECT_EQ(0, len);
+ EXPECT_STREQ(zeroCharString.c_str(), mValue);
+ ResetValue();
+}
+
+TEST_F(PropertiesTest, property_get_default_NULL) {
+ // Try to use a NULL default value => get returns 0
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
+
+ // Expect a return value of 0
+ int len = property_get(PROPERTY_TEST_KEY, mValue, NULL);
+ EXPECT_EQ(0, len);
+ ResetValue();
+}
+
+TEST_F(PropertiesTest, property_get_bool_0) {
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, "0"));
+ ASSERT_FALSE(property_get_bool(PROPERTY_TEST_KEY, true));
+}
+
+TEST_F(PropertiesTest, property_get_bool_1) {
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, "1"));
+ ASSERT_TRUE(property_get_bool(PROPERTY_TEST_KEY, false));
+}
+
+TEST_F(PropertiesTest, property_get_bool_false) {
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, "false"));
+ ASSERT_FALSE(property_get_bool(PROPERTY_TEST_KEY, true));
+}
+
+TEST_F(PropertiesTest, property_get_bool_n) {
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, "n"));
+ ASSERT_FALSE(property_get_bool(PROPERTY_TEST_KEY, true));
+}
+
+TEST_F(PropertiesTest, property_get_bool_no) {
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, "no"));
+ ASSERT_FALSE(property_get_bool(PROPERTY_TEST_KEY, true));
+}
+
+TEST_F(PropertiesTest, property_get_bool_off) {
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, "off"));
+ ASSERT_FALSE(property_get_bool(PROPERTY_TEST_KEY, true));
+}
+
+TEST_F(PropertiesTest, property_get_bool_on) {
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, "on"));
+ ASSERT_TRUE(property_get_bool(PROPERTY_TEST_KEY, false));
+}
+
+TEST_F(PropertiesTest, property_get_bool_true) {
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, "true"));
+ ASSERT_TRUE(property_get_bool(PROPERTY_TEST_KEY, false));
+}
+
+TEST_F(PropertiesTest, property_get_bool_y) {
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, "y"));
+ ASSERT_TRUE(property_get_bool(PROPERTY_TEST_KEY, false));
+}
+
+TEST_F(PropertiesTest, property_get_bool_yes) {
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, "yes"));
+ ASSERT_TRUE(property_get_bool(PROPERTY_TEST_KEY, false));
+}
+
+TEST_F(PropertiesTest, property_get_bool_neither) {
const char *valuesNeither[] = { "x0", "x1", "2", "-2", "True", "False", "garbage", "", " ",
"+1", " 1 ", " true", " true ", " y ", " yes", "yes ",
"+0", "-0", "00", " 00 ", " false", "false ",
@@ -263,7 +282,7 @@
}
}
-TEST_F(PropertiesTest, GetInt64) {
+TEST_F(PropertiesTest, property_get_int64) {
const int64_t DEFAULT_VALUE = INT64_C(0xDEADBEEFBEEFDEAD);
const std::string longMaxString = ToString(INT64_MAX);
@@ -310,7 +329,7 @@
}
}
-TEST_F(PropertiesTest, GetInt32) {
+TEST_F(PropertiesTest, property_get_int32) {
const int32_t DEFAULT_VALUE = INT32_C(0xDEADBEEF);
const std::string intMaxString = ToString(INT32_MAX);
diff --git a/libgrallocusage/Android.bp b/libgrallocusage/Android.bp
index ce0c3c8..33ae13d 100644
--- a/libgrallocusage/Android.bp
+++ b/libgrallocusage/Android.bp
@@ -26,4 +26,5 @@
export_include_dirs: ["include"],
shared_libs: ["android.hardware.graphics.allocator@2.0"],
header_libs: ["libhardware_headers"],
+ min_sdk_version: "29",
}
diff --git a/liblog/Android.bp b/liblog/Android.bp
index f1e5118..6051ac7 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -38,6 +38,11 @@
vendor_available: true,
ramdisk_available: true,
recovery_available: true,
+ apex_available: [
+ "//apex_available:platform",
+ "//apex_available:anyapex",
+ ],
+ min_sdk_version: "29",
native_bridge_supported: true,
export_include_dirs: ["include"],
system_shared_libs: [],
@@ -117,8 +122,12 @@
logtags: ["event.logtags"],
compile_multilib: "both",
apex_available: [
- "//apex_available:anyapex",
"//apex_available:platform",
+ // liblog is exceptionally available to the runtime APEX
+ // because the dynamic linker has to use it statically.
+ // See b/151051671
+ "com.android.runtime",
+ // DO NOT add more apex names here
],
}
diff --git a/liblog/README.md b/liblog/README.md
index 871399a..74a2cd7 100644
--- a/liblog/README.md
+++ b/liblog/README.md
@@ -60,8 +60,6 @@
LOG_EVENT_INT(tag, value)
LOG_EVENT_LONG(tag, value)
- clockid_t android_log_clockid()
-
log_id_t android_logger_get_id(struct logger *logger)
int android_logger_clear(struct logger *logger)
int android_logger_get_log_size(struct logger *logger)
@@ -118,10 +116,9 @@
finally a call closing the logs. A single log can be opened with `android_logger_list_open()`; or
multiple logs can be opened with `android_logger_list_alloc()`, calling in turn the
`android_logger_open()` for each log id. Each entry can be retrieved with
-`android_logger_list_read()`. The log(s) can be closed with `android_logger_list_free()`. The logs
-should be opened with an `ANDROID_LOG_RDONLY` mode. `ANDROID_LOG_NONBLOCK` mode will report when
-the log reading is done with an `EAGAIN` error return code, otherwise the
-`android_logger_list_read()` call will block for new entries.
+`android_logger_list_read()`. The log(s) can be closed with `android_logger_list_free()`.
+`ANDROID_LOG_NONBLOCK` mode will report when the log reading is done with an `EAGAIN` error return
+code, otherwise the `android_logger_list_read()` call will block for new entries.
The `ANDROID_LOG_WRAP` mode flag to the `android_logger_list_alloc_time()` signals logd to quiesce
the reader until the buffer is about to prune at the start time then proceed to dumping content.
@@ -130,14 +127,12 @@
logs to the persistent logs from before the last reboot.
The value returned by `android_logger_open()` can be used as a parameter to the
-`android_logger_clear()` function to empty the sub-log. It is recommended to only open log
-`ANDROID_LOG_WRONLY` in that case.
+`android_logger_clear()` function to empty the sub-log.
The value returned by `android_logger_open()` can be used as a parameter to the
`android_logger_get_log_(size|readable_size|version)` to retrieve the sub-log maximum size, readable
size and log buffer format protocol version respectively. `android_logger_get_id()` returns the id
-that was used when opening the sub-log. It is recommended to open the log `ANDROID_LOG_RDONLY` in
-these cases.
+that was used when opening the sub-log.
Errors
------
diff --git a/liblog/README.protocol.md b/liblog/README.protocol.md
index fef29c9..f247b28 100644
--- a/liblog/README.protocol.md
+++ b/liblog/README.protocol.md
@@ -17,6 +17,49 @@
};
};
+where the embedded structs are defined as:
+
+ struct android_log_header_t {
+ uint8_t id;
+ uint16_t tid;
+ log_time realtime;
+ };
+
+ struct log_time {
+ uint32_t tv_sec = 0;
+ uint32_t tv_nsec = 0;
+ }
+
+ struct android_event_header_t {
+ int32_t tag;
+ };
+
+ struct android_event_list_t {
+ int8_t type; // EVENT_TYPE_LIST
+ int8_t element_count;
+ };
+
+ struct android_event_float_t {
+ int8_t type; // EVENT_TYPE_FLOAT
+ float data;
+ };
+
+ struct android_event_int_t {
+ int8_t type; // EVENT_TYPE_INT
+ int32_t data;
+ } android_event_int_t;
+
+ struct android_event_long_t {
+ int8_t type; // EVENT_TYPE_LONG
+ int64_t data;
+ };
+
+ struct android_event_string_t {
+ int8_t type; // EVENT_TYPE_STRING;
+ int32_t length;
+ char data[];
+ };
+
The payload, excluding the header, has a max size of LOGGER_ENTRY_MAX_PAYLOAD.
## header
diff --git a/liblog/include/android/log.h b/liblog/include/android/log.h
index c98455d..8a0ebf2 100644
--- a/liblog/include/android/log.h
+++ b/liblog/include/android/log.h
@@ -56,6 +56,12 @@
#include <stdarg.h>
#include <stddef.h>
+#include <stdint.h>
+#include <sys/cdefs.h>
+
+#if !defined(__BIONIC__) && !defined(__INTRODUCED_IN)
+#define __INTRODUCED_IN(x)
+#endif
#ifdef __cplusplus
extern "C" {
@@ -149,14 +155,11 @@
/** The kernel log buffer. */
LOG_ID_KERNEL = 7,
- LOG_ID_MAX
-} log_id_t;
+ LOG_ID_MAX,
-/**
- * Let the logging function choose the best log target.
- * This is not part of the enum since adding either -1 or 0xFFFFFFFF forces the enum to be signed or
- * unsigned, which breaks unfortunately common arithmetic against LOG_ID_MIN and LOG_ID_MAX. */
-#define LOG_ID_DEFAULT -1
+ /** Let the logging function choose the best log target. */
+ LOG_ID_DEFAULT = 0x7FFFFFFF
+} log_id_t;
/**
* Writes the constant string `text` to the log buffer `id`,
@@ -181,72 +184,120 @@
* Logger data struct used for writing log messages to liblog via __android_log_write_logger_data()
* and sending log messages to user defined loggers specified in __android_log_set_logger().
*/
-struct __android_logger_data {
- size_t struct_size; /* Must be set to sizeof(__android_logger_data) and is used for versioning. */
- int buffer_id; /* log_id_t or -1 to represent 'default'. */
- int priority; /* android_LogPriority values. */
- const char* tag;
- const char* file; /* Optional file name, may be set to nullptr. */
- unsigned int line; /* Optional line number, ignore if file is nullptr. */
-};
+struct __android_log_message {
+ /** Must be set to sizeof(__android_log_message) and is used for versioning. */
+ size_t struct_size;
-/**
- * Writes the log message specified with logger_data and msg to the log. logger_data includes
- * additional file name and line number information that a logger may use. logger_data is versioned
- * for backwards compatibility.
- * This assumes that loggability has already been checked through __android_log_is_loggable().
- * Higher level logging libraries, such as libbase, first check loggability, then format their
- * buffers, then pass the message to liblog via this function, and therefore we do not want to
- * duplicate the loggability check here.
- */
-void __android_log_write_logger_data(struct __android_logger_data* logger_data, const char* msg);
+ /** {@link log_id_t} values. */
+ int32_t buffer_id;
+
+ /** {@link android_LogPriority} values. */
+ int32_t priority;
+
+ /** The tag for the log message. */
+ const char* tag;
+
+ /** Optional file name, may be set to nullptr. */
+ const char* file;
+
+ /** Optional line number, ignore if file is nullptr. */
+ uint32_t line;
+
+ /** The log message itself. */
+ const char* message;
+};
/**
* Prototype for the 'logger' function that is called for every log message.
*/
-typedef void (*__android_logger_function)(const struct __android_logger_data* logger_data,
- const char* message);
-
-/**
- * Sets a user defined logger function. All log messages sent to liblog will be set to the
- * function pointer specified by logger for processing.
- */
-void __android_log_set_logger(__android_logger_function logger);
-
-/**
- * Writes the log message to logd. This is an __android_logger_function and can be provided to
- * __android_log_set_logger(). It is the default logger when running liblog on a device.
- */
-void __android_log_logd_logger(const struct __android_logger_data* logger_data, const char* msg);
-
-/**
- * Writes the log message to stderr. This is an __android_logger_function and can be provided to
- * __android_log_set_logger(). It is the default logger when running liblog on host.
- */
-void __android_log_stderr_logger(const struct __android_logger_data* logger_data,
- const char* message);
-
+typedef void (*__android_logger_function)(const struct __android_log_message* log_message);
/**
* Prototype for the 'abort' function that is called when liblog will abort due to
* __android_log_assert() failures.
*/
typedef void (*__android_aborter_function)(const char* abort_message);
+#if !defined(__ANDROID__) || __ANDROID_API__ >= 30
/**
- * Sets a user defined aborter function that is called for __android_log_assert() failures.
+ * Writes the log message specified by log_message. log_message includes additional file name and
+ * line number information that a logger may use. log_message is versioned for backwards
+ * compatibility.
+ * This assumes that loggability has already been checked through __android_log_is_loggable().
+ * Higher level logging libraries, such as libbase, first check loggability, then format their
+ * buffers, then pass the message to liblog via this function, and therefore we do not want to
+ * duplicate the loggability check here.
+ *
+ * @param log_message the log message itself, see __android_log_message.
+ *
+ * Available since API level 30.
*/
-void __android_log_set_aborter(__android_aborter_function aborter);
+void __android_log_write_log_message(struct __android_log_message* log_message) __INTRODUCED_IN(30);
+
+/**
+ * Sets a user defined logger function. All log messages sent to liblog will be set to the
+ * function pointer specified by logger for processing. It is not expected that log messages are
+ * already terminated with a new line. This function should add new lines if required for line
+ * separation.
+ *
+ * @param logger the new function that will handle log messages.
+ *
+ * Available since API level 30.
+ */
+void __android_log_set_logger(__android_logger_function logger) __INTRODUCED_IN(30);
+
+/**
+ * Writes the log message to logd. This is an __android_logger_function and can be provided to
+ * __android_log_set_logger(). It is the default logger when running liblog on a device.
+ *
+ * @param log_message the log message to write, see __android_log_message.
+ *
+ * Available since API level 30.
+ */
+void __android_log_logd_logger(const struct __android_log_message* log_message) __INTRODUCED_IN(30);
+
+/**
+ * Writes the log message to stderr. This is an __android_logger_function and can be provided to
+ * __android_log_set_logger(). It is the default logger when running liblog on host.
+ *
+ * @param log_message the log message to write, see __android_log_message.
+ *
+ * Available since API level 30.
+ */
+void __android_log_stderr_logger(const struct __android_log_message* log_message)
+ __INTRODUCED_IN(30);
+
+/**
+ * Sets a user defined aborter function that is called for __android_log_assert() failures. This
+ * user defined aborter function is highly recommended to abort and be noreturn, but is not strictly
+ * required to.
+ *
+ * @param aborter the new aborter function, see __android_aborter_function.
+ *
+ * Available since API level 30.
+ */
+void __android_log_set_aborter(__android_aborter_function aborter) __INTRODUCED_IN(30);
/**
* Calls the stored aborter function. This allows for other logging libraries to use the same
* aborter function by calling this function in liblog.
+ *
+ * @param abort_message an additional message supplied when aborting, for example this is used to
+ * call android_set_abort_message() in __android_log_default_aborter().
+ *
+ * Available since API level 30.
*/
-void __android_log_call_aborter(const char* abort_message);
+void __android_log_call_aborter(const char* abort_message) __INTRODUCED_IN(30);
/**
* Sets android_set_abort_message() on device then aborts(). This is the default aborter.
+ *
+ * @param abort_message an additional message supplied when aborting. This functions calls
+ * android_set_abort_message() with its contents.
+ *
+ * Available since API level 30.
*/
-void __android_log_default_aborter(const char* abort_message);
+void __android_log_default_aborter(const char* abort_message) __attribute__((noreturn))
+__INTRODUCED_IN(30);
/**
* Use the per-tag properties "log.tag.<tagname>" along with the minimum priority from
@@ -258,30 +309,69 @@
* minimum priority needed to log. If only one is set, then that value is used to determine the
* minimum priority needed. If none are set, then default_priority is used.
*
- * prio is ANDROID_LOG_VERBOSE to ANDROID_LOG_FATAL.
+ * @param prio the priority to test, takes android_LogPriority values.
+ * @param tag the tag to test.
+ * @param default_prio the default priority to use if no properties or minimum priority are set.
+ * @return an integer where 1 indicates that the message is loggable and 0 indicates that it is not.
+ *
+ * Available since API level 30.
*/
-int __android_log_is_loggable(int prio, const char* tag, int default_prio);
-int __android_log_is_loggable_len(int prio, const char* tag, size_t len, int default_prio);
+int __android_log_is_loggable(int prio, const char* tag, int default_prio) __INTRODUCED_IN(30);
+
+/**
+ * Use the per-tag properties "log.tag.<tagname>" along with the minimum priority from
+ * __android_log_set_minimum_priority() to determine if a log message with a given prio and tag will
+ * be printed. A non-zero result indicates yes, zero indicates false.
+ *
+ * If both a priority for a tag and a minimum priority are set by
+ * __android_log_set_minimum_priority(), then the lowest of the two values are to determine the
+ * minimum priority needed to log. If only one is set, then that value is used to determine the
+ * minimum priority needed. If none are set, then default_priority is used.
+ *
+ * @param prio the priority to test, takes android_LogPriority values.
+ * @param tag the tag to test.
+ * @param len the length of the tag.
+ * @param default_prio the default priority to use if no properties or minimum priority are set.
+ * @return an integer where 1 indicates that the message is loggable and 0 indicates that it is not.
+ *
+ * Available since API level 30.
+ */
+int __android_log_is_loggable_len(int prio, const char* tag, size_t len, int default_prio)
+ __INTRODUCED_IN(30);
/**
* Sets the minimum priority that will be logged for this process.
*
- * This returns the previous set minimum priority, or ANDROID_LOG_DEFAULT if none was set.
+ * @param priority the new minimum priority to set, takes android_LogPriority values.
+ * @return the previous set minimum priority as android_LogPriority values, or
+ * ANDROID_LOG_DEFAULT if none was set.
+ *
+ * Available since API level 30.
*/
-int __android_log_set_minimum_priority(int priority);
+int32_t __android_log_set_minimum_priority(int32_t priority) __INTRODUCED_IN(30);
/**
* Gets the minimum priority that will be logged for this process. If none has been set by a
* previous __android_log_set_minimum_priority() call, this returns ANDROID_LOG_DEFAULT.
+ *
+ * @return the current minimum priority as android_LogPriority values, or
+ * ANDROID_LOG_DEFAULT if none is set.
+ *
+ * Available since API level 30.
*/
-int __android_log_get_minimum_priority(void);
+int32_t __android_log_get_minimum_priority(void) __INTRODUCED_IN(30);
/**
* Sets the default tag if no tag is provided when writing a log message. Defaults to
* getprogname(). This truncates tag to the maximum log message size, though appropriate tags
* should be much smaller.
+ *
+ * @param tag the new log tag.
+ *
+ * Available since API level 30.
*/
-void __android_log_set_default_tag(const char* tag);
+void __android_log_set_default_tag(const char* tag) __INTRODUCED_IN(30);
+#endif
#ifdef __cplusplus
}
diff --git a/liblog/include/log/log.h b/liblog/include/log/log.h
index 90d1e76..d7e9b7d 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.
*/
@@ -87,8 +93,6 @@
/*
* Event log entry types.
*/
-#ifndef __AndroidEventLogType_defined
-#define __AndroidEventLogType_defined
typedef enum {
/* Special markers for android_log_list_element type */
EVENT_TYPE_LIST_STOP = '\n', /* declare end of list */
@@ -101,9 +105,6 @@
EVENT_TYPE_LIST = 3,
EVENT_TYPE_FLOAT = 4,
} AndroidEventLogType;
-#endif
-#define sizeof_AndroidEventLogType sizeof(typeof_AndroidEventLogType)
-#define typeof_AndroidEventLogType unsigned char
#ifndef LOG_EVENT_INT
#define LOG_EVENT_INT(_tag, _value) \
@@ -132,19 +133,16 @@
(void)__android_log_bswrite(_tag, _value);
#endif
-#ifdef __linux__
-
-clockid_t android_log_clockid(void);
-
-#endif /* __linux__ */
-
/* --------------------------------------------------------------------- */
/*
* 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_id.h b/liblog/include/log/log_id.h
index c8fafe7..8e4faeb 100644
--- a/liblog/include/log/log_id.h
+++ b/liblog/include/log/log_id.h
@@ -23,14 +23,6 @@
#endif
/*
- * Send a simple string to the log.
- */
-int __android_log_buf_write(int bufID, int prio, const char* tag,
- const char* text);
-int __android_log_buf_print(int bufID, int prio, const char* tag, const char* fmt, ...)
- __attribute__((__format__(printf, 4, 5)));
-
-/*
* log_id_t helpers
*/
log_id_t android_name_to_log_id(const char* logName);
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_radio.h b/liblog/include/log/log_radio.h
index 8b8a362..f5525c1 100644
--- a/liblog/include/log/log_radio.h
+++ b/liblog/include/log/log_radio.h
@@ -17,7 +17,6 @@
#pragma once
#include <android/log.h>
-#include <log/log_id.h>
/*
* Normally we strip the effects of ALOGV (VERBOSE messages),
diff --git a/liblog/include/log/log_read.h b/liblog/include/log/log_read.h
index 18c1c33..23d76f4 100644
--- a/liblog/include/log/log_read.h
+++ b/liblog/include/log/log_read.h
@@ -16,30 +16,18 @@
#pragma once
+#include <stdint.h>
#include <sys/types.h>
-/* deal with possible sys/cdefs.h conflict with fcntl.h */
-#ifdef __unused
-#define __unused_defined __unused
-#undef __unused
-#endif
-
-#include <fcntl.h> /* Pick up O_* macros */
-
-/* restore definitions from above */
-#ifdef __unused_defined
-#define __unused __attribute__((__unused__))
-#endif
-
-#include <stdint.h>
-
-#include <log/log_id.h>
+#include <android/log.h>
#include <log/log_time.h>
#ifdef __cplusplus
extern "C" {
#endif
+#define ANDROID_LOG_WRAP_DEFAULT_TIMEOUT 7200 /* 2 hour default */
+
/*
* Native log reading interface section. See logcat for sample code.
*
@@ -48,8 +36,6 @@
* access to raw information, or parsing is an issue.
*/
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wzero-length-array"
struct logger_entry {
uint16_t len; /* length of the payload */
uint16_t hdr_size; /* sizeof(struct logger_entry) */
@@ -59,16 +45,7 @@
uint32_t nsec; /* nanoseconds */
uint32_t lid; /* log id of the payload, bottom 4 bits currently */
uint32_t uid; /* generating process's uid */
- char msg[0]; /* the entry's payload */
};
-#pragma clang diagnostic pop
-
-/*
- * 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.
@@ -83,32 +60,9 @@
struct logger_entry entry;
} __attribute__((aligned(4)));
#ifdef __cplusplus
- /* Matching log_time operators */
- bool operator==(const log_msg& T) const {
- return (entry.sec == T.entry.sec) && (entry.nsec == T.entry.nsec);
- }
- bool operator!=(const log_msg& T) const {
- return !(*this == T);
- }
- bool operator<(const log_msg& T) const {
- return (entry.sec < T.entry.sec) ||
- ((entry.sec == T.entry.sec) && (entry.nsec < T.entry.nsec));
- }
- bool operator>=(const log_msg& T) const {
- return !(*this < T);
- }
- bool operator>(const log_msg& T) const {
- return (entry.sec > T.entry.sec) ||
- ((entry.sec == T.entry.sec) && (entry.nsec > T.entry.nsec));
- }
- bool operator<=(const log_msg& T) const {
- return !(*this > T);
- }
uint64_t nsec() const {
return static_cast<uint64_t>(entry.sec) * NS_PER_SEC + entry.nsec;
}
-
- /* packet methods */
log_id_t id() {
return static_cast<log_id_t>(entry.lid);
}
@@ -141,17 +95,10 @@
char* buf, size_t len);
int android_logger_set_prune_list(struct logger_list* logger_list, const char* buf, size_t len);
-#define ANDROID_LOG_RDONLY O_RDONLY
-#define ANDROID_LOG_WRONLY O_WRONLY
-#define ANDROID_LOG_RDWR O_RDWR
-#define ANDROID_LOG_ACCMODE O_ACCMODE
-#ifndef O_NONBLOCK
+/* The below values are used for the `mode` argument of the below functions. */
+/* Note that 0x00000003 were previously used and should be considered reserved. */
#define ANDROID_LOG_NONBLOCK 0x00000800
-#else
-#define ANDROID_LOG_NONBLOCK O_NONBLOCK
-#endif
#define ANDROID_LOG_WRAP 0x40000000 /* Block until buffer about to wrap */
-#define ANDROID_LOG_WRAP_DEFAULT_TIMEOUT 7200 /* 2 hour default */
#define ANDROID_LOG_PSTORE 0x80000000
struct logger_list* android_logger_list_alloc(int mode, unsigned int tail,
@@ -165,7 +112,6 @@
/* Multiple log_id_t opens */
struct logger* android_logger_open(struct logger_list* logger_list, log_id_t id);
-#define android_logger_close android_logger_free
/* Single log_id_t open */
struct logger_list* android_logger_list_open(log_id_t id, int mode,
unsigned int tail, pid_t pid);
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/include/log/log_system.h b/liblog/include/log/log_system.h
index eaec741..6f40515 100644
--- a/liblog/include/log/log_system.h
+++ b/liblog/include/log/log_system.h
@@ -17,7 +17,6 @@
#pragma once
#include <android/log.h>
-#include <log/log_id.h>
/*
* Normally we strip the effects of ALOGV (VERBOSE messages),
diff --git a/liblog/include/log/log_time.h b/liblog/include/log/log_time.h
index 6b4458c..f50764d 100644
--- a/liblog/include/log/log_time.h
+++ b/liblog/include/log/log_time.h
@@ -37,9 +37,7 @@
uint32_t tv_sec = 0; /* good to Feb 5 2106 */
uint32_t tv_nsec = 0;
- static const uint32_t tv_sec_max = 0xFFFFFFFFUL;
- static const uint32_t tv_nsec_max = 999999999UL;
- static const timespec EPOCH;
+ static constexpr timespec EPOCH = {0, 0};
log_time() {}
explicit log_time(const timespec& T)
@@ -55,16 +53,6 @@
tv_nsec = static_cast<uint32_t>(T.tv_nsec);
}
#endif
- explicit log_time(const char* T) {
- const uint8_t* c = reinterpret_cast<const uint8_t*>(T);
- tv_sec = c[0] | (static_cast<uint32_t>(c[1]) << 8) |
- (static_cast<uint32_t>(c[2]) << 16) |
- (static_cast<uint32_t>(c[3]) << 24);
- tv_nsec = c[4] | (static_cast<uint32_t>(c[5]) << 8) |
- (static_cast<uint32_t>(c[6]) << 16) |
- (static_cast<uint32_t>(c[7]) << 24);
- }
-
/* timespec */
bool operator==(const timespec& T) const {
return (tv_sec == static_cast<uint32_t>(T.tv_sec)) &&
@@ -90,17 +78,6 @@
return !(*this > T);
}
- log_time operator-=(const timespec& T);
- log_time operator-(const timespec& T) const {
- log_time local(*this);
- return local -= T;
- }
- log_time operator+=(const timespec& T);
- log_time operator+(const timespec& T) const {
- log_time local(*this);
- return local += T;
- }
-
/* log_time */
bool operator==(const log_time& T) const {
return (tv_sec == T.tv_sec) && (tv_nsec == T.tv_nsec);
@@ -123,12 +100,36 @@
return !(*this > T);
}
- log_time operator-=(const log_time& T);
+ log_time operator-=(const log_time& T) {
+ // No concept of negative time, clamp to EPOCH
+ if (*this <= T) {
+ return *this = log_time(EPOCH);
+ }
+
+ if (this->tv_nsec < T.tv_nsec) {
+ --this->tv_sec;
+ this->tv_nsec = NS_PER_SEC + this->tv_nsec - T.tv_nsec;
+ } else {
+ this->tv_nsec -= T.tv_nsec;
+ }
+ this->tv_sec -= T.tv_sec;
+
+ return *this;
+ }
log_time operator-(const log_time& T) const {
log_time local(*this);
return local -= T;
}
- log_time operator+=(const log_time& T);
+ log_time operator+=(const log_time& T) {
+ this->tv_nsec += T.tv_nsec;
+ if (this->tv_nsec >= NS_PER_SEC) {
+ this->tv_nsec -= NS_PER_SEC;
+ ++this->tv_sec;
+ }
+ this->tv_sec += T.tv_sec;
+
+ return *this;
+ }
log_time operator+(const log_time& T) const {
log_time local(*this);
return local += T;
@@ -146,10 +147,8 @@
tv_nsec / (NS_PER_SEC / MS_PER_SEC);
}
- static const char default_format[];
-
/* Add %#q for the fraction of a second to the standard library functions */
- char* strptime(const char* s, const char* format = default_format);
+ char* strptime(const char* s, const char* format);
} __attribute__((__packed__));
}
diff --git a/liblog/include_vndk/log/log.h b/liblog/include_vndk/log/log.h
index a79beec..ab4adc4 100644
--- a/liblog/include_vndk/log/log.h
+++ b/liblog/include_vndk/log/log.h
@@ -3,6 +3,9 @@
#ifndef _LIBS_LOG_LOG_H
#define _LIBS_LOG_LOG_H
+/* Historically vendors have depended on this header being included. */
+#include <fcntl.h>
+
#include <android/log.h>
#include <log/log_id.h>
#include <log/log_main.h>
diff --git a/liblog/liblog.map.txt b/liblog/liblog.map.txt
index 198cdae..161fcf1 100644
--- a/liblog/liblog.map.txt
+++ b/liblog/liblog.map.txt
@@ -1,6 +1,6 @@
LIBLOG {
global:
- android_name_to_log_id; # llndk
+ android_name_to_log_id; # apex llndk
android_log_id_to_name; # llndk
__android_log_assert;
__android_log_buf_print;
@@ -22,7 +22,7 @@
android_logger_list_alloc; # apex llndk
android_logger_list_alloc_time; # apex llndk
android_logger_list_free; # apex llndk
- android_logger_list_open; # llndk
+ android_logger_list_open; # apex llndk
android_logger_list_read; # apex llndk
android_logger_open; # apex llndk
android_logger_set_log_size; # llndk
@@ -65,7 +65,7 @@
android_log_parser_reset; # llndk
};
-LIGLOG_R { # introduced=30
+LIBLOG_R { # introduced=30
global:
__android_log_call_aborter;
__android_log_default_aborter;
@@ -77,7 +77,7 @@
__android_log_set_logger;
__android_log_set_minimum_priority;
__android_log_stderr_logger;
- __android_log_write_logger_data;
+ __android_log_write_log_message;
};
LIBLOG_PRIVATE {
diff --git a/liblog/log_time.cpp b/liblog/log_time.cpp
index 3fbe1cb..14c408c 100644
--- a/liblog/log_time.cpp
+++ b/liblog/log_time.cpp
@@ -21,11 +21,7 @@
#include <private/android_logger.h>
-const char log_time::default_format[] = "%m-%d %H:%M:%S.%q";
-const timespec log_time::EPOCH = {0, 0};
-
// Add %#q for fractional seconds to standard strptime function
-
char* log_time::strptime(const char* s, const char* format) {
time_t now;
#ifdef __linux__
@@ -131,59 +127,3 @@
#endif
return ret;
}
-
-log_time log_time::operator-=(const timespec& T) {
- // No concept of negative time, clamp to EPOCH
- if (*this <= T) {
- return *this = log_time(EPOCH);
- }
-
- if (this->tv_nsec < (unsigned long int)T.tv_nsec) {
- --this->tv_sec;
- this->tv_nsec = NS_PER_SEC + this->tv_nsec - T.tv_nsec;
- } else {
- this->tv_nsec -= T.tv_nsec;
- }
- this->tv_sec -= T.tv_sec;
-
- return *this;
-}
-
-log_time log_time::operator+=(const timespec& T) {
- this->tv_nsec += (unsigned long int)T.tv_nsec;
- if (this->tv_nsec >= NS_PER_SEC) {
- this->tv_nsec -= NS_PER_SEC;
- ++this->tv_sec;
- }
- this->tv_sec += T.tv_sec;
-
- return *this;
-}
-
-log_time log_time::operator-=(const log_time& T) {
- // No concept of negative time, clamp to EPOCH
- if (*this <= T) {
- return *this = log_time(EPOCH);
- }
-
- if (this->tv_nsec < T.tv_nsec) {
- --this->tv_sec;
- this->tv_nsec = NS_PER_SEC + this->tv_nsec - T.tv_nsec;
- } else {
- this->tv_nsec -= T.tv_nsec;
- }
- this->tv_sec -= T.tv_sec;
-
- return *this;
-}
-
-log_time log_time::operator+=(const log_time& T) {
- this->tv_nsec += T.tv_nsec;
- if (this->tv_nsec >= NS_PER_SEC) {
- this->tv_nsec -= NS_PER_SEC;
- ++this->tv_sec;
- }
- this->tv_sec += T.tv_sec;
-
- return *this;
-}
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/logger_name.cpp b/liblog/logger_name.cpp
index 7d676f4..e72290e 100644
--- a/liblog/logger_name.cpp
+++ b/liblog/logger_name.cpp
@@ -41,7 +41,10 @@
}
static_assert(std::is_same<std::underlying_type<log_id_t>::type, uint32_t>::value,
- "log_id_t must be an unsigned int");
+ "log_id_t must be an uint32_t");
+
+static_assert(std::is_same<std::underlying_type<android_LogPriority>::type, uint32_t>::value,
+ "log_id_t must be an uint32_t");
log_id_t android_name_to_log_id(const char* logName) {
const char* b;
diff --git a/liblog/logger_write.cpp b/liblog/logger_write.cpp
index 1b6b0c6..22c7eca 100644
--- a/liblog/logger_write.cpp
+++ b/liblog/logger_write.cpp
@@ -27,7 +27,7 @@
#include <android/set_abort_message.h>
#endif
-#include <shared_mutex>
+#include <atomic>
#include <android-base/errno_restorer.h>
#include <android-base/macros.h>
@@ -37,7 +37,6 @@
#include "android/log.h"
#include "log/log_read.h"
#include "logger.h"
-#include "rwlock.h"
#include "uio.h"
#ifdef __ANDROID__
@@ -141,21 +140,17 @@
static std::string default_tag = getprogname();
return default_tag;
}
-RwLock default_tag_lock;
void __android_log_set_default_tag(const char* tag) {
- auto lock = std::unique_lock{default_tag_lock};
GetDefaultTag().assign(tag, 0, LOGGER_ENTRY_MAX_PAYLOAD);
}
-static int minimum_log_priority = ANDROID_LOG_DEFAULT;
-int __android_log_set_minimum_priority(int priority) {
- int old_minimum_log_priority = minimum_log_priority;
- minimum_log_priority = priority;
- return old_minimum_log_priority;
+static std::atomic_int32_t minimum_log_priority = ANDROID_LOG_DEFAULT;
+int32_t __android_log_set_minimum_priority(int32_t priority) {
+ return minimum_log_priority.exchange(priority, std::memory_order_relaxed);
}
-int __android_log_get_minimum_priority() {
+int32_t __android_log_get_minimum_priority() {
return minimum_log_priority;
}
@@ -164,10 +159,8 @@
#else
static __android_logger_function logger_function = __android_log_stderr_logger;
#endif
-static RwLock logger_function_lock;
void __android_log_set_logger(__android_logger_function logger) {
- auto lock = std::unique_lock{logger_function_lock};
logger_function = logger;
}
@@ -181,15 +174,12 @@
}
static __android_aborter_function aborter_function = __android_log_default_aborter;
-static RwLock aborter_function_lock;
void __android_log_set_aborter(__android_aborter_function aborter) {
- auto lock = std::unique_lock{aborter_function_lock};
aborter_function = aborter;
}
void __android_log_call_aborter(const char* abort_message) {
- auto lock = std::shared_lock{aborter_function_lock};
aborter_function(abort_message);
}
@@ -202,7 +192,7 @@
return -EINVAL;
}
- clock_gettime(android_log_clockid(), &ts);
+ clock_gettime(CLOCK_REALTIME, &ts);
if (log_id == LOG_ID_SECURITY) {
if (vec[0].iov_len < 4) {
@@ -251,8 +241,7 @@
#endif
}
-void __android_log_stderr_logger(const struct __android_logger_data* logger_data,
- const char* message) {
+void __android_log_stderr_logger(const struct __android_log_message* log_message) {
struct tm now;
time_t t = time(nullptr);
@@ -268,34 +257,33 @@
static const char log_characters[] = "XXVDIWEF";
static_assert(arraysize(log_characters) - 1 == ANDROID_LOG_SILENT,
"Mismatch in size of log_characters and values in android_LogPriority");
- int priority =
- logger_data->priority > ANDROID_LOG_SILENT ? ANDROID_LOG_FATAL : logger_data->priority;
+ int32_t priority =
+ log_message->priority > ANDROID_LOG_SILENT ? ANDROID_LOG_FATAL : log_message->priority;
char priority_char = log_characters[priority];
uint64_t tid = GetThreadId();
- if (logger_data->file != nullptr) {
+ if (log_message->file != nullptr) {
fprintf(stderr, "%s %c %s %5d %5" PRIu64 " %s:%u] %s\n",
- logger_data->tag ? logger_data->tag : "nullptr", priority_char, timestamp, getpid(),
- tid, logger_data->file, logger_data->line, message);
+ log_message->tag ? log_message->tag : "nullptr", priority_char, timestamp, getpid(),
+ tid, log_message->file, log_message->line, log_message->message);
} else {
fprintf(stderr, "%s %c %s %5d %5" PRIu64 " %s\n",
- logger_data->tag ? logger_data->tag : "nullptr", priority_char, timestamp, getpid(),
- tid, message);
+ log_message->tag ? log_message->tag : "nullptr", priority_char, timestamp, getpid(),
+ tid, log_message->message);
}
}
-void __android_log_logd_logger(const struct __android_logger_data* logger_data,
- const char* message) {
- int buffer_id = logger_data->buffer_id == LOG_ID_DEFAULT ? LOG_ID_MAIN : logger_data->buffer_id;
+void __android_log_logd_logger(const struct __android_log_message* log_message) {
+ int buffer_id = log_message->buffer_id == LOG_ID_DEFAULT ? LOG_ID_MAIN : log_message->buffer_id;
struct iovec vec[3];
vec[0].iov_base =
- const_cast<unsigned char*>(reinterpret_cast<const unsigned char*>(&logger_data->priority));
+ const_cast<unsigned char*>(reinterpret_cast<const unsigned char*>(&log_message->priority));
vec[0].iov_len = 1;
- vec[1].iov_base = const_cast<void*>(static_cast<const void*>(logger_data->tag));
- vec[1].iov_len = strlen(logger_data->tag) + 1;
- vec[2].iov_base = const_cast<void*>(static_cast<const void*>(message));
- vec[2].iov_len = strlen(message) + 1;
+ vec[1].iov_base = const_cast<void*>(static_cast<const void*>(log_message->tag));
+ vec[1].iov_len = strlen(log_message->tag) + 1;
+ vec[2].iov_base = const_cast<void*>(static_cast<const void*>(log_message->message));
+ vec[2].iov_len = strlen(log_message->message) + 1;
write_to_log(static_cast<log_id_t>(buffer_id), vec, 3);
}
@@ -304,40 +292,38 @@
return __android_log_buf_write(LOG_ID_MAIN, prio, tag, msg);
}
-void __android_log_write_logger_data(__android_logger_data* logger_data, const char* msg) {
+void __android_log_write_log_message(__android_log_message* log_message) {
ErrnoRestorer errno_restorer;
- if (logger_data->buffer_id != LOG_ID_DEFAULT && logger_data->buffer_id != LOG_ID_MAIN &&
- logger_data->buffer_id != LOG_ID_SYSTEM && logger_data->buffer_id != LOG_ID_RADIO &&
- logger_data->buffer_id != LOG_ID_CRASH) {
+ if (log_message->buffer_id != LOG_ID_DEFAULT && log_message->buffer_id != LOG_ID_MAIN &&
+ log_message->buffer_id != LOG_ID_SYSTEM && log_message->buffer_id != LOG_ID_RADIO &&
+ log_message->buffer_id != LOG_ID_CRASH) {
return;
}
- auto tag_lock = std::shared_lock{default_tag_lock, std::defer_lock};
- if (logger_data->tag == nullptr) {
- tag_lock.lock();
- logger_data->tag = GetDefaultTag().c_str();
+ if (log_message->tag == nullptr) {
+ log_message->tag = GetDefaultTag().c_str();
}
#if __BIONIC__
- if (logger_data->priority == ANDROID_LOG_FATAL) {
- android_set_abort_message(msg);
+ if (log_message->priority == ANDROID_LOG_FATAL) {
+ android_set_abort_message(log_message->message);
}
#endif
- auto lock = std::shared_lock{logger_function_lock};
- logger_function(logger_data, msg);
+ logger_function(log_message);
}
int __android_log_buf_write(int bufID, int prio, const char* tag, const char* msg) {
ErrnoRestorer errno_restorer;
if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
- return 0;
+ return -EPERM;
}
- __android_logger_data logger_data = {sizeof(__android_logger_data), bufID, prio, tag, nullptr, 0};
- __android_log_write_logger_data(&logger_data, msg);
+ __android_log_message log_message = {
+ sizeof(__android_log_message), bufID, prio, tag, nullptr, 0, msg};
+ __android_log_write_log_message(&log_message);
return 1;
}
@@ -345,16 +331,16 @@
ErrnoRestorer errno_restorer;
if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
- return 0;
+ return -EPERM;
}
__attribute__((uninitialized)) char buf[LOG_BUF_SIZE];
vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
- __android_logger_data logger_data = {
- sizeof(__android_logger_data), LOG_ID_MAIN, prio, tag, nullptr, 0};
- __android_log_write_logger_data(&logger_data, buf);
+ __android_log_message log_message = {
+ sizeof(__android_log_message), LOG_ID_MAIN, prio, tag, nullptr, 0, buf};
+ __android_log_write_log_message(&log_message);
return 1;
}
@@ -362,7 +348,7 @@
ErrnoRestorer errno_restorer;
if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
- return 0;
+ return -EPERM;
}
va_list ap;
@@ -372,9 +358,9 @@
vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
va_end(ap);
- __android_logger_data logger_data = {
- sizeof(__android_logger_data), LOG_ID_MAIN, prio, tag, nullptr, 0};
- __android_log_write_logger_data(&logger_data, buf);
+ __android_log_message log_message = {
+ sizeof(__android_log_message), LOG_ID_MAIN, prio, tag, nullptr, 0, buf};
+ __android_log_write_log_message(&log_message);
return 1;
}
@@ -382,7 +368,7 @@
ErrnoRestorer errno_restorer;
if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
- return 0;
+ return -EPERM;
}
va_list ap;
@@ -392,8 +378,9 @@
vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
va_end(ap);
- __android_logger_data logger_data = {sizeof(__android_logger_data), bufID, prio, tag, nullptr, 0};
- __android_log_write_logger_data(&logger_data, buf);
+ __android_log_message log_message = {
+ sizeof(__android_log_message), bufID, prio, tag, nullptr, 0, buf};
+ __android_log_write_log_message(&log_message);
return 1;
}
diff --git a/liblog/logger_write.h b/liblog/logger_write.h
index 065fd55..eee2778 100644
--- a/liblog/logger_write.h
+++ b/liblog/logger_write.h
@@ -18,7 +18,4 @@
#include <string>
-#include "rwlock.h"
-
-std::string& GetDefaultTag(); // Must read lock default_tag_lock
-extern RwLock default_tag_lock;
\ No newline at end of file
+std::string& GetDefaultTag();
diff --git a/liblog/logprint.cpp b/liblog/logprint.cpp
index e32878a..238431f 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
@@ -214,11 +216,7 @@
p_ret->year_output = false;
p_ret->zone_output = false;
p_ret->epoch_output = false;
-#ifdef __ANDROID__
- p_ret->monotonic_output = android_log_clockid() == CLOCK_MONOTONIC;
-#else
p_ret->monotonic_output = false;
-#endif
p_ret->uid_output = false;
p_ret->descriptive_output = false;
descriptive_output = false;
@@ -509,12 +507,12 @@
* format: <priority:1><tag:N>\0<message:N>\0
*
* tag str
- * starts at buf->msg+1
+ * starts at buf + buf->hdr_size + 1
* msg
- * starts at buf->msg+1+len(tag)+1
+ * starts at buf + buf->hdr_size + 1 + len(tag) + 1
*
- * The message may have been truncated by the kernel log driver.
- * When that happens, we must null-terminate the message ourselves.
+ * The message may have been truncated. When that happens, we must null-terminate the message
+ * ourselves.
*/
if (buf->len < 3) {
/*
@@ -529,11 +527,11 @@
int msgEnd = -1;
int i;
- char* msg = buf->msg;
- if (buf->hdr_size != sizeof(struct logger_entry)) {
- fprintf(stderr, "+++ LOG: entry illegal hdr_size\n");
+ if (buf->hdr_size < sizeof(logger_entry)) {
+ fprintf(stderr, "+++ LOG: hdr_size must be at least as big as struct logger_entry\n");
return -1;
}
+ char* msg = reinterpret_cast<char*>(buf) + buf->hdr_size;
entry->uid = buf->uid;
for (i = 1; i < buf->len; i++) {
@@ -985,11 +983,11 @@
entry->pid = buf->pid;
entry->tid = buf->tid;
- eventData = (const unsigned char*)buf->msg;
- if (buf->hdr_size != sizeof(struct logger_entry)) {
- fprintf(stderr, "+++ LOG: entry illegal hdr_size\n");
+ if (buf->hdr_size < sizeof(logger_entry)) {
+ fprintf(stderr, "+++ LOG: hdr_size must be at least as big as struct logger_entry\n");
return -1;
}
+ eventData = reinterpret_cast<unsigned char*>(buf) + buf->hdr_size;
if (buf->lid == LOG_ID_SECURITY) {
entry->priority = ANDROID_LOG_WARN;
}
@@ -1048,7 +1046,7 @@
}
if ((result == 1) && fmtStr) {
/* We overflowed :-(, let's repaint the line w/o format dressings */
- eventData = (const unsigned char*)buf->msg;
+ eventData = reinterpret_cast<unsigned char*>(buf) + buf->hdr_size;
eventData += 4;
outBuf = messageBuf;
outRemaining = messageBufLen - 1;
@@ -1463,13 +1461,10 @@
nsec = entry->tv_nsec;
#if __ANDROID__
if (p_format->monotonic_output) {
- /* prevent convertMonotonic from being called if logd is monotonic */
- if (android_log_clockid() != CLOCK_MONOTONIC) {
- struct timespec time;
- convertMonotonic(&time, entry);
- now = time.tv_sec;
- nsec = time.tv_nsec;
- }
+ struct timespec time;
+ convertMonotonic(&time, entry);
+ now = time.tv_sec;
+ nsec = time.tv_nsec;
}
#endif
if (now < 0) {
diff --git a/liblog/pmsg_reader.cpp b/liblog/pmsg_reader.cpp
index 64a92b7..0e39aab 100644
--- a/liblog/pmsg_reader.cpp
+++ b/liblog/pmsg_reader.cpp
@@ -96,7 +96,7 @@
((logger_list->start.tv_sec != buf.l.realtime.tv_sec) ||
(logger_list->start.tv_nsec <= buf.l.realtime.tv_nsec)))) &&
(!logger_list->pid || (logger_list->pid == buf.p.pid))) {
- char* msg = log_msg->entry.msg;
+ char* msg = reinterpret_cast<char*>(&log_msg->entry) + log_msg->entry.hdr_size;
*msg = buf.prio;
fd = atomic_load(&logger_list->fd);
if (fd <= 0) {
@@ -185,7 +185,7 @@
/* Add just enough clues in logger_list and transp to make API function */
memset(&logger_list, 0, sizeof(logger_list));
- logger_list.mode = ANDROID_LOG_PSTORE | ANDROID_LOG_NONBLOCK | ANDROID_LOG_RDONLY;
+ logger_list.mode = ANDROID_LOG_PSTORE | ANDROID_LOG_NONBLOCK;
logger_list.log_mask = (unsigned)-1;
if (logId != LOG_ID_ANY) {
logger_list.log_mask = (1 << logId);
diff --git a/liblog/pmsg_writer.cpp b/liblog/pmsg_writer.cpp
index 06e5e04..8e676bd 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;
@@ -188,7 +188,7 @@
return -EINVAL;
}
- clock_gettime(android_log_clockid(), &ts);
+ clock_gettime(CLOCK_REALTIME, &ts);
cp = strdup(filename);
if (!cp) {
diff --git a/liblog/properties.cpp b/liblog/properties.cpp
index abd48fc..f5e060c 100644
--- a/liblog/properties.cpp
+++ b/liblog/properties.cpp
@@ -23,7 +23,6 @@
#include <unistd.h>
#include <algorithm>
-#include <shared_mutex>
#include <private/android_logger.h>
@@ -99,9 +98,7 @@
static const char log_namespace[] = "persist.log.tag.";
static const size_t base_offset = 8; /* skip "persist." */
- auto tag_lock = std::shared_lock{default_tag_lock, std::defer_lock};
if (tag == nullptr || len == 0) {
- tag_lock.lock();
auto& tag_string = GetDefaultTag();
tag = tag_string.c_str();
len = tag_string.size();
@@ -368,29 +365,6 @@
return c;
}
-static unsigned char evaluate_persist_ro(const struct cache2_char* self) {
- unsigned char c = self->cache_persist.c;
-
- if (c) {
- return c;
- }
-
- return self->cache_ro.c;
-}
-
-/*
- * Timestamp state generally remains constant, but can change at any time
- * to handle developer requirements.
- */
-clockid_t android_log_clockid() {
- static struct cache2_char clockid = {PTHREAD_MUTEX_INITIALIZER, 0,
- "persist.logd.timestamp", {{NULL, 0xFFFFFFFF}, '\0'},
- "ro.logd.timestamp", {{NULL, 0xFFFFFFFF}, '\0'},
- evaluate_persist_ro};
-
- return (tolower(do_cache2_char(&clockid)) == 'm') ? CLOCK_MONOTONIC : CLOCK_REALTIME;
-}
-
/*
* Security state generally remains constant, but the DO must be able
* to turn off logging should it become spammy after an attack is detected.
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 b4bb77f..2a6424b 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",
@@ -72,6 +72,7 @@
],
static_libs: ["liblog"],
isolated: true,
+ require_root: true,
}
// Build tests for the device (with .so). Run with:
@@ -96,7 +97,7 @@
cflags: ["-DNO_PSTORE"],
test_suites: [
"cts",
- "vts",
+ "vts10",
],
}
@@ -108,7 +109,6 @@
"liblog_host_test.cpp",
"liblog_default_tag.cpp",
"liblog_global_state.cpp",
- "rwlock_test.cpp",
],
isolated: true,
}
diff --git a/liblog/tests/libc_test.cpp b/liblog/tests/libc_test.cpp
index 3534eb8..1f26263 100644
--- a/liblog/tests/libc_test.cpp
+++ b/liblog/tests/libc_test.cpp
@@ -22,6 +22,10 @@
TEST(libc, __pstore_append) {
#ifdef __ANDROID__
#ifndef NO_PSTORE
+ if (access("/dev/pmsg0", W_OK) != 0) {
+ GTEST_SKIP() << "pmsg0 not found, skipping test";
+ }
+
FILE* fp;
ASSERT_TRUE(NULL != (fp = fopen("/dev/pmsg0", "ae")));
static const char message[] = "libc.__pstore_append\n";
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index 4366f3d..3bd5cf2 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();
@@ -183,7 +184,7 @@
*/
struct timespec ts;
- clock_gettime(android_log_clockid(), &ts);
+ clock_gettime(CLOCK_REALTIME, &ts);
android_pmsg_log_header_t pmsg_header;
pmsg_header.magic = LOGGER_MAGIC;
@@ -259,7 +260,7 @@
*/
struct timespec ts;
- clock_gettime(android_log_clockid(), &ts);
+ clock_gettime(CLOCK_REALTIME, &ts);
struct packet {
android_pmsg_log_header_t pmsg_header;
@@ -334,7 +335,7 @@
*/
struct timespec ts;
- clock_gettime(android_log_clockid(), &ts);
+ clock_gettime(CLOCK_REALTIME, &ts);
struct packet {
android_pmsg_log_header_t pmsg_header;
@@ -409,7 +410,7 @@
*/
struct timespec ts;
- clock_gettime(android_log_clockid(), &ts);
+ clock_gettime(CLOCK_REALTIME, &ts);
struct packet {
android_pmsg_log_header_t pmsg_header;
@@ -482,7 +483,7 @@
*/
struct timespec ts;
- clock_gettime(android_log_clockid(), &ts);
+ clock_gettime(CLOCK_REALTIME, &ts);
struct packet {
android_pmsg_log_header_t pmsg_header;
@@ -648,8 +649,7 @@
static void BM_log_latency(benchmark::State& state) {
pid_t pid = getpid();
- struct logger_list* logger_list =
- android_logger_list_open(LOG_ID_EVENTS, ANDROID_LOG_RDONLY, 0, pid);
+ struct logger_list* logger_list = android_logger_list_open(LOG_ID_EVENTS, 0, 0, pid);
if (!logger_list) {
fprintf(stderr, "Unable to open events log: %s\n", strerror(errno));
@@ -684,8 +684,8 @@
if (!eventData || (eventData[4] != EVENT_TYPE_LONG)) {
continue;
}
- log_time tx(eventData + 4 + 1);
- if (ts != tx) {
+ log_time* tx = reinterpret_cast<log_time*>(eventData + 4 + 1);
+ if (ts != *tx) {
if (0xDEADBEEFA55A5AA5ULL == caught_convert(eventData + 4 + 1)) {
state.SkipWithError("signal");
break;
@@ -723,8 +723,7 @@
static void BM_log_delay(benchmark::State& state) {
pid_t pid = getpid();
- struct logger_list* logger_list =
- android_logger_list_open(LOG_ID_EVENTS, ANDROID_LOG_RDONLY, 0, pid);
+ struct logger_list* logger_list = android_logger_list_open(LOG_ID_EVENTS, 0, 0, pid);
if (!logger_list) {
fprintf(stderr, "Unable to open events log: %s\n", strerror(errno));
@@ -758,8 +757,8 @@
if (!eventData || (eventData[4] != EVENT_TYPE_LONG)) {
continue;
}
- log_time tx(eventData + 4 + 1);
- if (ts != tx) {
+ log_time* tx = reinterpret_cast<log_time*>(eventData + 4 + 1);
+ if (ts != *tx) {
if (0xDEADBEEFA55A5AA6ULL == caught_convert(eventData + 4 + 1)) {
state.SkipWithError("signal");
break;
@@ -793,16 +792,6 @@
BENCHMARK(BM_is_loggable);
/*
- * Measure the time it takes for android_log_clockid.
- */
-static void BM_clockid(benchmark::State& state) {
- while (state.KeepRunning()) {
- android_log_clockid();
- }
-}
-BENCHMARK(BM_clockid);
-
-/*
* Measure the time it takes for __android_log_security.
*/
static void BM_security(benchmark::State& state) {
diff --git a/liblog/tests/liblog_global_state.cpp b/liblog/tests/liblog_global_state.cpp
index 9a181ef..3508818 100644
--- a/liblog/tests/liblog_global_state.cpp
+++ b/liblog/tests/liblog_global_state.cpp
@@ -59,16 +59,15 @@
static unsigned int expected_line;
static std::string expected_message = "libbase test message";
- auto liblog_logger_function = [](const struct __android_logger_data* logger_data,
- const char* message) {
+ auto liblog_logger_function = [](const struct __android_log_message* log_message) {
message_seen = true;
- EXPECT_EQ(sizeof(__android_logger_data), logger_data->struct_size);
- EXPECT_EQ(LOG_ID_DEFAULT, logger_data->buffer_id);
- EXPECT_EQ(ANDROID_LOG_WARN, logger_data->priority);
- EXPECT_STREQ(LOG_TAG, logger_data->tag);
- EXPECT_EQ(expected_file, logger_data->file);
- EXPECT_EQ(expected_line, logger_data->line);
- EXPECT_EQ(expected_message, message);
+ EXPECT_EQ(sizeof(__android_log_message), log_message->struct_size);
+ EXPECT_EQ(LOG_ID_DEFAULT, log_message->buffer_id);
+ EXPECT_EQ(ANDROID_LOG_WARN, log_message->priority);
+ EXPECT_STREQ(LOG_TAG, log_message->tag);
+ EXPECT_EQ(expected_file, log_message->file);
+ EXPECT_EQ(expected_line, log_message->line);
+ EXPECT_EQ(expected_message, log_message->message);
};
__android_log_set_logger(liblog_logger_function);
@@ -111,16 +110,15 @@
static int expected_priority = ANDROID_LOG_WARN;
static std::string expected_message = "libbase test message";
- auto liblog_logger_function = [](const struct __android_logger_data* logger_data,
- const char* message) {
+ auto liblog_logger_function = [](const struct __android_log_message* log_message) {
message_seen = true;
- EXPECT_EQ(sizeof(__android_logger_data), logger_data->struct_size);
- EXPECT_EQ(expected_buffer_id, logger_data->buffer_id);
- EXPECT_EQ(expected_priority, logger_data->priority);
- EXPECT_STREQ(LOG_TAG, logger_data->tag);
- EXPECT_STREQ(nullptr, logger_data->file);
- EXPECT_EQ(0U, logger_data->line);
- EXPECT_EQ(expected_message, message);
+ EXPECT_EQ(sizeof(__android_log_message), log_message->struct_size);
+ EXPECT_EQ(expected_buffer_id, log_message->buffer_id);
+ EXPECT_EQ(expected_priority, log_message->priority);
+ EXPECT_STREQ(LOG_TAG, log_message->tag);
+ EXPECT_STREQ(nullptr, log_message->file);
+ EXPECT_EQ(0U, log_message->line);
+ EXPECT_EQ(expected_message, log_message->message);
};
__android_log_set_logger(liblog_logger_function);
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index a60d2df..fbc3d7a 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>
@@ -82,7 +83,7 @@
pid_t pid = getpid();
auto logger_list = std::unique_ptr<struct logger_list, ListCloser>{
- android_logger_list_open(log_buffer, ANDROID_LOG_RDONLY, 1000, pid)};
+ android_logger_list_open(log_buffer, 0, 1000, pid)};
ASSERT_TRUE(logger_list);
write_messages();
@@ -97,16 +98,13 @@
ASSERT_EQ(log_buffer, log_msg.id());
ASSERT_EQ(pid, log_msg.entry.pid);
- // TODO: Should this be an assert?
- if (log_msg.msg() == nullptr) {
- continue;
- }
+ ASSERT_NE(nullptr, log_msg.msg());
check_message(log_msg, &found);
}
auto logger_list_non_block = std::unique_ptr<struct logger_list, ListCloser>{
- android_logger_list_open(log_buffer, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)};
+ android_logger_list_open(log_buffer, ANDROID_LOG_NONBLOCK, 1000, pid)};
ASSERT_TRUE(logger_list_non_block);
size_t count = 0;
@@ -121,10 +119,7 @@
ASSERT_EQ(log_buffer, log_msg.id());
ASSERT_EQ(pid, log_msg.entry.pid);
- // TODO: Should this be an assert?
- if (log_msg.msg() == nullptr) {
- continue;
- }
+ ASSERT_NE(nullptr, log_msg.msg());
found = false;
check_message(log_msg, &found);
@@ -160,7 +155,6 @@
return ret;
}
-#ifndef NO_PSTORE
static bool isPmsgActive() {
pid_t pid = getpid();
@@ -170,7 +164,6 @@
return std::string::npos != myPidFds.find(" -> /dev/pmsg0");
}
-#endif /* NO_PSTORE */
static bool isLogdwActive() {
std::string logdwSignature =
@@ -222,16 +215,18 @@
log_time ts(CLOCK_MONOTONIC);
log_time ts1(ts);
+ bool has_pstore = access("/dev/pmsg0", W_OK) == 0;
+
auto write_function = [&] {
EXPECT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
// Check that we can close and reopen the logger
bool logdwActiveAfter__android_log_btwrite;
if (getuid() == AID_ROOT) {
tested__android_log_close = true;
-#ifndef NO_PSTORE
- bool pmsgActiveAfter__android_log_btwrite = isPmsgActive();
- EXPECT_TRUE(pmsgActiveAfter__android_log_btwrite);
-#endif /* NO_PSTORE */
+ if (has_pstore) {
+ bool pmsgActiveAfter__android_log_btwrite = isPmsgActive();
+ EXPECT_TRUE(pmsgActiveAfter__android_log_btwrite);
+ }
logdwActiveAfter__android_log_btwrite = isLogdwActive();
EXPECT_TRUE(logdwActiveAfter__android_log_btwrite);
} else if (!tested__android_log_close) {
@@ -239,10 +234,10 @@
}
__android_log_close();
if (getuid() == AID_ROOT) {
-#ifndef NO_PSTORE
- bool pmsgActiveAfter__android_log_close = isPmsgActive();
- EXPECT_FALSE(pmsgActiveAfter__android_log_close);
-#endif /* NO_PSTORE */
+ if (has_pstore) {
+ bool pmsgActiveAfter__android_log_close = isPmsgActive();
+ EXPECT_FALSE(pmsgActiveAfter__android_log_close);
+ }
bool logdwActiveAfter__android_log_close = isLogdwActive();
EXPECT_FALSE(logdwActiveAfter__android_log_close);
}
@@ -250,10 +245,10 @@
ts1 = log_time(CLOCK_MONOTONIC);
EXPECT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts1, sizeof(ts1)));
if (getuid() == AID_ROOT) {
-#ifndef NO_PSTORE
- bool pmsgActiveAfter__android_log_btwrite = isPmsgActive();
- EXPECT_TRUE(pmsgActiveAfter__android_log_btwrite);
-#endif /* NO_PSTORE */
+ if (has_pstore) {
+ bool pmsgActiveAfter__android_log_btwrite = isPmsgActive();
+ EXPECT_TRUE(pmsgActiveAfter__android_log_btwrite);
+ }
logdwActiveAfter__android_log_btwrite = isLogdwActive();
EXPECT_TRUE(logdwActiveAfter__android_log_btwrite);
}
@@ -275,10 +270,10 @@
return;
}
- log_time tx(reinterpret_cast<char*>(&eventData->payload.data));
- if (ts == tx) {
+ log_time* tx = reinterpret_cast<log_time*>(&eventData->payload.data);
+ if (ts == *tx) {
++count;
- } else if (ts1 == tx) {
+ } else if (ts1 == *tx) {
++second_count;
}
@@ -334,8 +329,6 @@
#ifdef __ANDROID__
pid_t pid = getpid();
- log_time ts(android_log_clockid());
-
size_t num_lines = 1, size = 0, length = 0, total = 0;
const char* cp = message;
while (*cp) {
@@ -437,7 +430,6 @@
pid_t pid = getpid();
static const char tag[] = "TEST__android_log_buf_write";
- log_time ts(android_log_clockid());
auto write_function = [&] {
EXPECT_LT(0, __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_INFO, tag, message));
@@ -572,8 +564,7 @@
v += pid & 0xFFFF;
- ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
- LOG_ID_EVENTS, ANDROID_LOG_RDONLY, 1000, pid)));
+ ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(LOG_ID_EVENTS, 0, 1000, pid)));
int count = 0;
@@ -728,8 +719,7 @@
v += pid & 0xFFFF;
- ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
- LOG_ID_EVENTS, ANDROID_LOG_RDONLY, 1000, pid)));
+ ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(LOG_ID_EVENTS, 0, 1000, pid)));
int count = 0;
@@ -1033,7 +1023,7 @@
#endif
}
-// TODO: This test is tautological. android_logger_list_read() calls recv() with
+// Note: This test is tautological. android_logger_list_read() calls recv() with
// LOGGER_ENTRY_MAX_PAYLOAD as its size argument, so it's not possible for this test to read a
// payload larger than that size.
TEST(liblog, too_big_payload) {
@@ -1093,11 +1083,11 @@
pid_t pid = getpid();
auto logger_list1 = std::unique_ptr<struct logger_list, ListCloser>{
- android_logger_list_open(LOG_ID_MAIN, ANDROID_LOG_RDONLY, expected_count1, pid)};
+ android_logger_list_open(LOG_ID_MAIN, 0, expected_count1, pid)};
ASSERT_TRUE(logger_list1);
auto logger_list2 = std::unique_ptr<struct logger_list, ListCloser>{
- android_logger_list_open(LOG_ID_MAIN, ANDROID_LOG_RDONLY, expected_count2, pid)};
+ android_logger_list_open(LOG_ID_MAIN, 0, expected_count2, pid)};
ASSERT_TRUE(logger_list2);
for (int i = 25; i > 0; --i) {
@@ -1128,14 +1118,12 @@
}
// Test again with the nonblocking reader.
- auto logger_list_non_block1 =
- std::unique_ptr<struct logger_list, ListCloser>{android_logger_list_open(
- LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, expected_count1, pid)};
+ auto logger_list_non_block1 = std::unique_ptr<struct logger_list, ListCloser>{
+ android_logger_list_open(LOG_ID_MAIN, ANDROID_LOG_NONBLOCK, expected_count1, pid)};
ASSERT_TRUE(logger_list_non_block1);
- auto logger_list_non_block2 =
- std::unique_ptr<struct logger_list, ListCloser>{android_logger_list_open(
- LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, expected_count2, pid)};
+ auto logger_list_non_block2 = std::unique_ptr<struct logger_list, ListCloser>{
+ android_logger_list_open(LOG_ID_MAIN, ANDROID_LOG_NONBLOCK, expected_count2, pid)};
ASSERT_TRUE(logger_list_non_block2);
count1 = 0;
count2 = 0;
@@ -1542,8 +1530,8 @@
pid_t pid = getpid();
- struct logger_list* logger_list = android_logger_list_open(
- LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid);
+ struct logger_list* logger_list =
+ android_logger_list_open(LOG_ID_EVENTS, ANDROID_LOG_NONBLOCK, 1000, pid);
int count = 0;
if (logger_list == NULL) return count;
@@ -1832,10 +1820,8 @@
gid = getgid();
pid_t pid = getpid();
- ASSERT_TRUE(NULL !=
- (logger_list = android_logger_list_open(
- LOG_ID_SECURITY, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK,
- 1000, pid)));
+ ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(LOG_ID_SECURITY, ANDROID_LOG_NONBLOCK,
+ 1000, pid)));
log_time ts(CLOCK_MONOTONIC);
@@ -1988,7 +1974,6 @@
#endif
}
-// TODO: Do we need to check that we didn't actually write anything if we return a failure here?
TEST(liblog,
android_errorWriteWithInfoLog__android_logger_list_read__null_data) {
#ifdef __ANDROID__
diff --git a/liblog/tests/log_read_test.cpp b/liblog/tests/log_read_test.cpp
index 1be99aa..3e09617 100644
--- a/liblog/tests/log_read_test.cpp
+++ b/liblog/tests/log_read_test.cpp
@@ -34,8 +34,7 @@
// This test assumes the log buffers are filled with noise from
// normal operations. It will fail if done immediately after a
// logcat -c.
- struct logger_list* logger_list =
- android_logger_list_alloc(ANDROID_LOG_WRONLY, 0, 0);
+ struct logger_list* logger_list = android_logger_list_alloc(0, 0, 0);
for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
log_id_t id = static_cast<log_id_t>(i);
diff --git a/liblog/tests/log_wrap_test.cpp b/liblog/tests/log_wrap_test.cpp
index e06964f..755898a 100644
--- a/liblog/tests/log_wrap_test.cpp
+++ b/liblog/tests/log_wrap_test.cpp
@@ -32,7 +32,7 @@
static void read_with_wrap() {
// Read the last line in the log to get a starting timestamp. We're assuming
// the log is not empty.
- const int mode = ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
+ const int mode = ANDROID_LOG_NONBLOCK;
struct logger_list* logger_list =
android_logger_list_open(LOG_ID_MAIN, mode, 1000, 0);
diff --git a/liblog/tests/logd_writer_test.cpp b/liblog/tests/logd_writer_test.cpp
new file mode 100644
index 0000000..b8e4726
--- /dev/null
+++ b/liblog/tests/logd_writer_test.cpp
@@ -0,0 +1,99 @@
+/*
+ * 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__
+ if (getuid() != 0) {
+ GTEST_SKIP() << "Skipping test, must be run as root.";
+ return;
+ }
+ 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/logprint_test.cpp b/liblog/tests/logprint_test.cpp
index 7ca02ac..72e53f9 100644
--- a/liblog/tests/logprint_test.cpp
+++ b/liblog/tests/logprint_test.cpp
@@ -14,8 +14,14 @@
* limitations under the License.
*/
+#include <log/logprint.h>
+
+#include <string>
+
#include <gtest/gtest.h>
+#include <log/log_read.h>
+
size_t convertPrintable(char* p, const char* message, size_t messageLen);
TEST(liblog, convertPrintable_ascii) {
@@ -85,3 +91,63 @@
EXPECT_EQ(output_size, strlen(expected_output));
EXPECT_STREQ(expected_output, output);
}
+
+TEST(liblog, log_print_different_header_size) {
+ constexpr int32_t kPid = 123;
+ constexpr uint32_t kTid = 456;
+ constexpr uint32_t kSec = 1000;
+ constexpr uint32_t kNsec = 999;
+ constexpr uint32_t kLid = LOG_ID_MAIN;
+ constexpr uint32_t kUid = 987;
+ constexpr char kPriority = ANDROID_LOG_ERROR;
+
+ auto create_buf = [](char* buf, size_t len, uint16_t hdr_size) {
+ memset(buf, 0, len);
+ logger_entry* header = reinterpret_cast<logger_entry*>(buf);
+ header->hdr_size = hdr_size;
+ header->pid = kPid;
+ header->tid = kTid;
+ header->sec = kSec;
+ header->nsec = kNsec;
+ header->lid = kLid;
+ header->uid = kUid;
+ char* message = buf + header->hdr_size;
+ uint16_t message_len = 0;
+ message[message_len++] = kPriority;
+ message[message_len++] = 'T';
+ message[message_len++] = 'a';
+ message[message_len++] = 'g';
+ message[message_len++] = '\0';
+ message[message_len++] = 'm';
+ message[message_len++] = 's';
+ message[message_len++] = 'g';
+ message[message_len++] = '!';
+ message[message_len++] = '\0';
+ header->len = message_len;
+ };
+
+ auto check_entry = [&](const AndroidLogEntry& entry) {
+ EXPECT_EQ(kSec, static_cast<uint32_t>(entry.tv_sec));
+ EXPECT_EQ(kNsec, static_cast<uint32_t>(entry.tv_nsec));
+ EXPECT_EQ(kPriority, entry.priority);
+ EXPECT_EQ(kUid, static_cast<uint32_t>(entry.uid));
+ EXPECT_EQ(kPid, entry.pid);
+ EXPECT_EQ(kTid, static_cast<uint32_t>(entry.tid));
+ EXPECT_STREQ("Tag", entry.tag);
+ EXPECT_EQ(4U, entry.tagLen); // Apparently taglen includes the nullptr?
+ EXPECT_EQ(4U, entry.messageLen);
+ EXPECT_STREQ("msg!", entry.message);
+ };
+ alignas(logger_entry) char buf[LOGGER_ENTRY_MAX_LEN];
+ create_buf(buf, sizeof(buf), sizeof(logger_entry));
+
+ AndroidLogEntry entry_normal_size;
+ ASSERT_EQ(0,
+ android_log_processLogBuffer(reinterpret_cast<logger_entry*>(buf), &entry_normal_size));
+ check_entry(entry_normal_size);
+
+ create_buf(buf, sizeof(buf), sizeof(logger_entry) + 3);
+ AndroidLogEntry entry_odd_size;
+ ASSERT_EQ(0, android_log_processLogBuffer(reinterpret_cast<logger_entry*>(buf), &entry_odd_size));
+ check_entry(entry_odd_size);
+}
\ 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/libmodprobe/include/modprobe/modprobe.h b/libmodprobe/include/modprobe/modprobe.h
index ee6ae7a..a7687af 100644
--- a/libmodprobe/include/modprobe/modprobe.h
+++ b/libmodprobe/include/modprobe/modprobe.h
@@ -24,7 +24,7 @@
class Modprobe {
public:
- Modprobe(const std::vector<std::string>&);
+ Modprobe(const std::vector<std::string>&, const std::string load_file = "modules.load");
bool LoadListedModules(bool strict = true);
bool LoadWithAliases(const std::string& module_name, bool strict,
@@ -34,6 +34,8 @@
bool GetAllDependencies(const std::string& module, std::vector<std::string>* pre_dependencies,
std::vector<std::string>* dependencies,
std::vector<std::string>* post_dependencies);
+ void ResetModuleCount() { module_count_ = 0; }
+ int GetModuleCount() { return module_count_; }
void EnableBlacklist(bool enable);
void EnableVerbose(bool enable);
@@ -65,5 +67,6 @@
std::unordered_map<std::string, std::string> module_options_;
std::set<std::string> module_blacklist_;
std::unordered_set<std::string> module_loaded_;
+ int module_count_ = 0;
bool blacklist_enabled = false;
};
diff --git a/libmodprobe/libmodprobe.cpp b/libmodprobe/libmodprobe.cpp
index f22bbf1..d193796 100644
--- a/libmodprobe/libmodprobe.cpp
+++ b/libmodprobe/libmodprobe.cpp
@@ -312,7 +312,7 @@
}
}
-Modprobe::Modprobe(const std::vector<std::string>& base_paths) {
+Modprobe::Modprobe(const std::vector<std::string>& base_paths, const std::string load_file) {
using namespace std::placeholders;
for (const auto& base_path : base_paths) {
@@ -326,7 +326,7 @@
ParseCfg(base_path + "/modules.softdep", softdep_callback);
auto load_callback = std::bind(&Modprobe::ParseLoadCallback, this, _1);
- ParseCfg(base_path + "/modules.load", load_callback);
+ ParseCfg(base_path + "/" + load_file, load_callback);
auto options_callback = std::bind(&Modprobe::ParseOptionsCallback, this, _1);
ParseCfg(base_path + "/modules.options", options_callback);
diff --git a/libmodprobe/libmodprobe_ext.cpp b/libmodprobe/libmodprobe_ext.cpp
index 99472c1..6589708 100644
--- a/libmodprobe/libmodprobe_ext.cpp
+++ b/libmodprobe/libmodprobe_ext.cpp
@@ -63,6 +63,7 @@
LOG(INFO) << "Loaded kernel module " << path_name;
module_loaded_.emplace(canonical_name);
+ module_count_++;
return true;
}
diff --git a/libmodprobe/libmodprobe_ext_test.cpp b/libmodprobe/libmodprobe_ext_test.cpp
index 057dea3..9ee5ba7 100644
--- a/libmodprobe/libmodprobe_ext_test.cpp
+++ b/libmodprobe/libmodprobe_ext_test.cpp
@@ -56,6 +56,7 @@
}
modules_loaded.emplace_back(path_name + options);
+ module_count_++;
return true;
}
diff --git a/libmodprobe/libmodprobe_test.cpp b/libmodprobe/libmodprobe_test.cpp
index 879c7f2..eea0abd 100644
--- a/libmodprobe/libmodprobe_test.cpp
+++ b/libmodprobe/libmodprobe_test.cpp
@@ -161,6 +161,7 @@
EXPECT_TRUE(modules_loaded == expected_modules_loaded);
+ EXPECT_TRUE(m.GetModuleCount() == 15);
EXPECT_TRUE(m.Remove("test4"));
GTEST_LOG_(INFO) << "Expected modules loaded after removing test4 (in order):";
diff --git a/libnetutils/packet.c b/libnetutils/packet.c
index 9ecdd4f..64de00e 100644
--- a/libnetutils/packet.c
+++ b/libnetutils/packet.c
@@ -37,25 +37,22 @@
#include "dhcpmsg.h"
-int fatal();
+int fatal(const char*);
-int open_raw_socket(const char *ifname __attribute__((unused)), uint8_t *hwaddr, int if_index)
-{
- int s;
- struct sockaddr_ll bindaddr;
+int open_raw_socket(const char* ifname __unused, uint8_t hwaddr[ETH_ALEN], int if_index) {
+ int s = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+ if (s < 0) return fatal("socket(PF_PACKET)");
- if((s = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) {
- return fatal("socket(PF_PACKET)");
- }
-
- memset(&bindaddr, 0, sizeof(bindaddr));
- bindaddr.sll_family = AF_PACKET;
- bindaddr.sll_protocol = htons(ETH_P_IP);
- bindaddr.sll_halen = ETH_ALEN;
+ struct sockaddr_ll bindaddr = {
+ .sll_family = AF_PACKET,
+ .sll_protocol = htons(ETH_P_IP),
+ .sll_ifindex = if_index,
+ .sll_halen = ETH_ALEN,
+ };
memcpy(bindaddr.sll_addr, hwaddr, ETH_ALEN);
- bindaddr.sll_ifindex = if_index;
if (bind(s, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) < 0) {
+ close(s);
return fatal("Cannot bind raw socket to interface");
}
diff --git a/libnetutils/packet.h b/libnetutils/packet.h
index aade392..66186fc 100644
--- a/libnetutils/packet.h
+++ b/libnetutils/packet.h
@@ -17,7 +17,9 @@
#ifndef _WIFI_PACKET_H_
#define _WIFI_PACKET_H_
-int open_raw_socket(const char *ifname, uint8_t *hwaddr, int if_index);
+#include <linux/if_ether.h>
+
+int open_raw_socket(const char* ifname, uint8_t hwaddr[ETH_ALEN], int if_index);
int send_packet(int s, int if_index, struct dhcp_msg *msg, int size,
uint32_t saddr, uint32_t daddr, uint32_t sport, uint32_t dport);
int receive_packet(int s, struct dhcp_msg *msg);
diff --git a/libpixelflinger/Android.bp b/libpixelflinger/Android.bp
index 76d9444..45a32da 100644
--- a/libpixelflinger/Android.bp
+++ b/libpixelflinger/Android.bp
@@ -93,23 +93,5 @@
"arch-arm64/t32cb16blend.S",
],
},
- mips: {
- mips32r6: {
- srcs: [
- "codeflinger/MIPSAssembler.cpp",
- "codeflinger/mips_disassem.c",
- "arch-mips/t32cb16blend.S",
- ],
- },
- },
- mips64: {
- srcs: [
- "codeflinger/MIPSAssembler.cpp",
- "codeflinger/MIPS64Assembler.cpp",
- "codeflinger/mips64_disassem.c",
- "arch-mips64/col32cb16blend.S",
- "arch-mips64/t32cb16blend.S",
- ],
- },
},
}
diff --git a/libpixelflinger/arch-mips/col32cb16blend.S b/libpixelflinger/arch-mips/col32cb16blend.S
deleted file mode 100644
index 810294c..0000000
--- a/libpixelflinger/arch-mips/col32cb16blend.S
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
-** Copyright 2015, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
- .macro pixel dreg src f sR sG sB shift
-
-#if __mips==32 && __mips_isa_rev>=2
- /* extract red */
- ext $t4,\src,\shift+11,5
- mul $t4,$t4,\f
-
- /* extract green */
- ext $t5,\src,\shift+5,6
- mul $t5,$t5,\f
-
- /* extract blue */
- ext $t6,\src,\shift,5
- mul $t6,$t6,\f
-#else
- /* extract red */
- srl $t4,\src,\shift+11
- andi $t4, 0x1f
- mul $t4,$t4,\f
-
- /* extract green */
- srl $t5,\src,\shift+5
- andi $t5, 0x3f
- mul $t5,$t5,\f
-
- /* extract blue */
- srl $t6,\src,\shift
- andi $t6, 0x1f
- mul $t6,$t6,\f
-#endif
-
- srl $t4,$t4,8
- srl $t5,$t5,8
- srl $t6,$t6,8
- addu $t4,$t4,\sR
- addu $t5,$t5,\sG
- addu \dreg,$t6,\sB
- sll $t4,$t4,11
- sll $t5,$t5,5
- or \dreg,\dreg,$t4
- or \dreg,\dreg,$t5
- andi \dreg, 0xffff
- .endm
-
- .text
- .balign 4
-
- .global scanline_col32cb16blend_mips
- .ent scanline_col32cb16blend_mips
-scanline_col32cb16blend_mips:
-
- /* check if count is zero */
- srl $v0,$a1,24 /* sA */
- beqz $a2,done
- li $t4, 0x100
- srl $v1,$v0,7
- addu $v0,$v1,$v0
- subu $v0,$t4,$v0 /* f */
-#if __mips==32 && __mips_isa_rev>=2
- ext $a3,$a1,3,5 /* sR */
- ext $t0,$a1,10,6 /* sG */
- ext $t1,$a1,19,5 /* sB */
-#else
- srl $a3, $a1, 3
- andi $a3, 0x1f /* sR */
- srl $t0, $a1, 10
- andi $t0, 0x3f /* sG */
- srl $t1, $a1, 19
- andi $t1, 0x1f /* sB */
-#endif
-
- /* check if cnt is at least 4 */
- addiu $a2,$a2,-4
- bltz $a2,tail
-
-loop_4pixels:
- lw $t7,0($a0)
- lw $t8,4($a0)
- addiu $a0,$a0,8
- addiu $a2,$a2,-4
- pixel $t2 $t7 $v0 $a3 $t0 $t1 0
- pixel $t3 $t7 $v0 $a3 $t0 $t1 16
-#if __mips==32 && __mips_isa_rev>=2
- ins $t2,$t3,16,16
-#else
- sll $t3, 16
- or $t2, $t2, $t3
-#endif
- pixel $t7 $t8 $v0 $a3 $t0 $t1 0
- pixel $t3 $t8 $v0 $a3 $t0 $t1 16
-#if __mips==32 && __mips_isa_rev>=2
- ins $t7,$t3,16,16
-#else
- sll $t3, 16
- or $t7, $t7, $t3
-#endif
- sw $t2,-8($a0)
- sw $t7,-4($a0)
- bgez $a2, loop_4pixels
-
-tail:
- /* the pixel count underran, restore it now */
- addiu $a2,$a2,4
-
- /* handle the last 0..3 pixels */
- beqz $a2,done
-
-loop_1pixel:
- lhu $t7,0($a0)
- addiu $a0,$a0,2
- addiu $a2,$a2,-1
- pixel $t2 $t7 $v0 $a3 $t0 $t1 0
- sh $t2, -2($a0)
- bnez $a2,loop_1pixel
-
-done:
- j $ra
- .end scanline_col32cb16blend_mips
diff --git a/libpixelflinger/arch-mips/t32cb16blend.S b/libpixelflinger/arch-mips/t32cb16blend.S
deleted file mode 100644
index 1d2fb8f..0000000
--- a/libpixelflinger/arch-mips/t32cb16blend.S
+++ /dev/null
@@ -1,273 +0,0 @@
-/* libs/pixelflinger/t32cb16blend.S
-**
-** Copyright 2010, 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.
-*/
-
-#ifdef DEBUG
-#define DBG
-#else
-#define DBG #
-#endif
-
-/*
- * blend one of 2 16bpp RGB pixels held in dreg selected by shift
- * with the 32bpp ABGR pixel held in src and store the result in fb
- *
- * Assumes that the dreg data is little endian and that
- * the the second pixel (shift==16) will be merged into
- * the fb result
- *
- * Uses $t0,$t6,$t7,$t8
- */
-
-#if __mips==32 && __mips_isa_rev>=2
- .macro pixel dreg src fb shift
- /*
- * sA = s >> 24
- * f = 0x100 - (sA + (sA>>7))
- */
-DBG .set noat
-DBG rdhwr $at,$2
-DBG .set at
-
- srl $t7,\src,24
- srl $t6,$t7,7
- addu $t7,$t6
- li $t6,0x100
- subu $t7,$t6,$t7
-
- /* red */
- ext $t8,\dreg,\shift+6+5,5 # dst[\shift:15..11]
- mul $t6,$t8,$t7
- ext $t0,\dreg,\shift+5,6 # start green extraction dst[\shift:10..5]
- ext $t8,\src,3,5 # src[7..3]
- srl $t6,8
- addu $t8,$t6
-.if \shift!=0
- sll $t8,\shift+11
- or \fb,$t8
-.else
- sll \fb,$t8,11
-.endif
-
- /* green */
- mul $t8,$t0,$t7
- ext $t0,\dreg,\shift,5 # start blue extraction dst[\shift:4..0]
- ext $t6,\src,2+8,6 # src[15..10]
- srl $t8,8
- addu $t8,$t6
-
- /* blue */
- mul $t0,$t0,$t7
- sll $t8, $t8, \shift+5
- or \fb, \fb, $t8
- ext $t6,\src,(3+8+8),5
- srl $t8,$t0,8
- addu $t8,$t6
- sll $t8, $t8, \shift
- or \fb, \fb, $t8
-
-DBG .set noat
-DBG rdhwr $t8,$2
-DBG subu $t8,$at
-DBG sltu $at,$t8,$v0
-DBG movn $v0,$t8,$at
-DBG sgtu $at,$t8,$v1
-DBG movn $v1,$t8,$at
-DBG .set at
- .endm
-
-#else
-
- .macro pixel dreg src fb shift
- /*
- * sA = s >> 24
- * f = 0x100 - (sA + (sA>>7))
- */
-DBG .set push
-DBG .set noat
-DBG .set mips32r2
-DBG rdhwr $at,$2
-DBG .set pop
-
- srl $t7,\src,24
- srl $t6,$t7,7
- addu $t7,$t6
- li $t6,0x100
- subu $t7,$t6,$t7
-
- /*
- * red
- * dR = (d >> (6 + 5)) & 0x1f;
- * dR = (f*dR)>>8
- * sR = (s >> ( 3)) & 0x1f;
- * sR += dR
- * fb |= sR << 11
- */
- srl $t8,\dreg,\shift+6+5
-.if \shift==0
- and $t8,0x1f
-.endif
- mul $t8,$t8,$t7
- srl $t6,\src,3
- and $t6,0x1f
- srl $t8,8
- addu $t8,$t6
-.if \shift!=0
- sll $t8,\shift+11
- or \fb,$t8
-.else
- sll \fb,$t8,11
-.endif
-
- /*
- * green
- * dG = (d >> 5) & 0x3f
- * dG = (f*dG) >> 8
- * sG = (s >> ( 8+2))&0x3F;
- */
- srl $t8,\dreg,\shift+5
- and $t8,0x3f
- mul $t8,$t8,$t7
- srl $t6,\src,8+2
- and $t6,0x3f
- srl $t8,8
- addu $t8,$t6
- sll $t8,\shift + 5
- or \fb,$t8
-
- /* blue */
-.if \shift!=0
- srl $t8,\dreg,\shift
- and $t8,0x1f
-.else
- and $t8,\dreg,0x1f
-.endif
- mul $t8,$t8,$t7
- srl $t6,\src,(8+8+3)
- and $t6,0x1f
- srl $t8,8
- addu $t8,$t6
-.if \shift!=0
- sll $t8,\shift
-.endif
- or \fb,$t8
-DBG .set push
-DBG .set noat
-DBG .set mips32r2
-DBG rdhwr $t8,$2
-DBG subu $t8,$at
-DBG sltu $at,$t8,$v0
-DBG movn $v0,$t8,$at
-DBG sgtu $at,$t8,$v1
-DBG movn $v1,$t8,$at
-DBG .set pop
- .endm
-#endif
-
- .text
- .balign 4
-
- .global scanline_t32cb16blend_mips
- .ent scanline_t32cb16blend_mips
-scanline_t32cb16blend_mips:
-DBG li $v0,0xffffffff
-DBG li $v1,0
- /* Align the destination if necessary */
- and $t0,$a0,3
- beqz $t0,aligned
-
- /* as long as there is at least one pixel */
- beqz $a2,done
-
- lw $t4,($a1)
- addu $a0,2
- addu $a1,4
- beqz $t4,1f
- lhu $t3,-2($a0)
- pixel $t3,$t4,$t1,0
- sh $t1,-2($a0)
-1: subu $a2,1
-
-aligned:
- /* Check to see if its worth unrolling the loop */
- subu $a2,4
- bltz $a2,tail
-
- /* Process 4 pixels at a time */
-fourpixels:
- /* 1st pair of pixels */
- lw $t4,0($a1)
- lw $t5,4($a1)
- addu $a0,8
- addu $a1,16
-
- /* both are zero, skip this pair */
- or $t3,$t4,$t5
- beqz $t3,1f
-
- /* load the destination */
- lw $t3,-8($a0)
-
- pixel $t3,$t4,$t1,0
- andi $t1, 0xFFFF
- pixel $t3,$t5,$t1,16
- sw $t1,-8($a0)
-
-1:
- /* 2nd pair of pixels */
- lw $t4,-8($a1)
- lw $t5,-4($a1)
-
- /* both are zero, skip this pair */
- or $t3,$t4,$t5
- beqz $t3,1f
-
- /* load the destination */
- lw $t3,-4($a0)
-
- pixel $t3,$t4,$t1,0
- andi $t1, 0xFFFF
- pixel $t3,$t5,$t1,16
- sw $t1,-4($a0)
-
-1: subu $a2,4
- bgtz $a2,fourpixels
-
-tail:
- /* the pixel count underran, restore it now */
- addu $a2,4
-
- /* handle the last 0..3 pixels */
- beqz $a2,done
-onepixel:
- lw $t4,($a1)
- addu $a0,2
- addu $a1,4
- beqz $t4,1f
- lhu $t3,-2($a0)
- pixel $t3,$t4,$t1,0
- sh $t1,-2($a0)
-1: subu $a2,1
- bnez $a2,onepixel
-done:
-DBG .set push
-DBG .set mips32r2
-DBG rdhwr $a0,$3
-DBG mul $v0,$a0
-DBG mul $v1,$a0
-DBG .set pop
- j $ra
- .end scanline_t32cb16blend_mips
diff --git a/libpixelflinger/arch-mips64/col32cb16blend.S b/libpixelflinger/arch-mips64/col32cb16blend.S
deleted file mode 100644
index 5baffb1..0000000
--- a/libpixelflinger/arch-mips64/col32cb16blend.S
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
-** Copyright 2015, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
- .macro pixel dreg src f sR sG sB shift
-
- /* extract red */
-.if \shift < 32
- dext $t0,\src,\shift+11,5
-.else
- dextu $t0,\src,\shift+11,5
-.endif
- mul $t0,$t0,\f
-
- /* extract green */
-.if \shift < 32
- dext $t1,\src,\shift+5,6
-.else
- dextu $t1,\src,\shift+5,6
-.endif
- mul $t1,$t1,\f
-
- /* extract blue */
-.if \shift < 32
- dext $t2,\src,\shift,5
-.else
- dextu $t2,\src,\shift,5
-.endif
- mul $t2,$t2,\f
-
- srl $t0,$t0,8
- srl $t1,$t1,8
- srl $t2,$t2,8
- addu $t0,$t0,\sR
- addu $t1,$t1,\sG
- addu \dreg,$t2,\sB
- sll $t0,$t0,11
- sll $t1,$t1,5
- or \dreg,\dreg,$t0
- or \dreg,\dreg,$t1
- .endm
-
- .text
- .balign 4
-
- .global scanline_col32cb16blend_mips64
- .ent scanline_col32cb16blend_mips64
-scanline_col32cb16blend_mips64:
-
- /* check if count is zero */
- srl $v0,$a1,24 /* sA */
- beqz $a2,done
- li $t0, 0x100
- srl $v1,$v0,7
- addu $v0,$v1,$v0
- subu $v0,$t0,$v0 /* f */
- ext $a3,$a1,3,5 /* sR */
- ext $a4,$a1,10,6 /* sG */
- ext $a5,$a1,19,5 /* sB */
-
- /* check if cnt is at least 4 */
- addiu $a2,$a2,-4
- bltz $a2,tail
-
-loop_4pixels:
- ld $t3,0($a0)
- daddiu $a0,$a0,8
- addiu $a2,$a2,-4
- pixel $a6 $t3 $v0 $a3 $a4 $a5 0
- pixel $a7 $t3 $v0 $a3 $a4 $a5 16
- pixel $t8 $t3 $v0 $a3 $a4 $a5 32
- pixel $t9 $t3 $v0 $a3 $a4 $a5 48
- dins $a6,$a7,16,16
- dinsu $a6,$t8,32,16
- dinsu $a6,$t9,48,16
- sd $a6,-8($a0)
- bgez $a2, loop_4pixels
-
-tail:
- /* the pixel count underran, restore it now */
- addiu $a2,$a2,4
-
- /* handle the last 0..3 pixels */
- beqz $a2,done
-
-loop_1pixel:
- lhu $t3,0($a0)
- daddiu $a0,$a0,2
- addiu $a2,$a2,-1
- pixel $a6 $t3 $v0 $a3 $a4 $a5 0
- sh $a6, -2($a0)
- bnez $a2,loop_1pixel
-
-done:
- j $ra
- .end scanline_col32cb16blend_mips64
diff --git a/libpixelflinger/arch-mips64/t32cb16blend.S b/libpixelflinger/arch-mips64/t32cb16blend.S
deleted file mode 100644
index 3cb5f93..0000000
--- a/libpixelflinger/arch-mips64/t32cb16blend.S
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
-** Copyright 2015, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#ifdef DEBUG
-#define DBG
-#else
-#define DBG #
-#endif
-
-/*
- * blend one of 2 16bpp RGB pixels held in dreg selected by shift
- * with the 32bpp ABGR pixel held in src and store the result in fb
- *
- * Assumes that the dreg data is little endian and that
- * the the second pixel (shift==16) will be merged into
- * the fb result
- *
- * Uses $a4,$t2,$t3,$t8
- */
-
- .macro pixel dreg src fb shift
- /*
- * sA = s >> 24
- * f = 0x100 - (sA + (sA>>7))
- */
- srl $t3,\src,24
- srl $t2,$t3,7
- addu $t3,$t2
- li $t2,0x100
- subu $t3,$t2,$t3
-
- /* red */
- ext $t8,\dreg,\shift+6+5,5 # dst[\shift:15..11]
- mul $t2,$t8,$t3
- ext $a4,\dreg,\shift+5,6 # start green extraction dst[\shift:10..5]
- ext $t8,\src,3,5 # src[7..3]
- srl $t2,8
- addu $t8,$t2
-.if \shift!=0
- sll $t8,\shift+11 # dst[\shift:15..11]
- or \fb,$t8
-.else
- sll \fb,$t8,11
-.endif
-
- /* green */
- mul $t8,$a4,$t3
- ext $a4,\dreg,\shift,5 # start blue extraction dst[\shift:4..0]
- ext $t2,\src,2+8,6 # src[15..10]
- srl $t8,8
- addu $t8,$t2
-
- /* blue */
- mul $a4,$a4,$t3
- sll $t8, $t8, \shift+5 # finish green insertion dst[\shift:10..5]
- or \fb, \fb, $t8
- ext $t2,\src,(3+8+8),5
- srl $t8,$a4,8
- addu $t8,$t2
- sll $t8, $t8, \shift
- or \fb, \fb, $t8
- .endm
-
- .text
- .balign 4
-
- .global scanline_t32cb16blend_mips64
- .ent scanline_t32cb16blend_mips64
-scanline_t32cb16blend_mips64:
- daddiu $sp, $sp, -40
-DBG li $v0,0xffffffff
-DBG li $v1,0
- /* Align the destination if necessary */
- and $a4,$a0,3
- beqz $a4,aligned
-
- /* as long as there is at least one pixel */
- beqz $a2,done
-
- lw $t0,($a1)
- daddu $a0,2
- daddu $a1,4
- beqz $t0,1f
- lhu $a7,-2($a0)
- pixel $a7,$t0,$a5,0
- sh $a5,-2($a0)
-1: subu $a2,1
-
-aligned:
- /* Check to see if its worth unrolling the loop */
- subu $a2,4
- bltz $a2,tail
-
- /* Process 4 pixels at a time */
-fourpixels:
- /* 1st pair of pixels */
- lw $t0,0($a1)
- lw $t1,4($a1)
- daddu $a0,8
- daddu $a1,16
-
- /* both are zero, skip this pair */
- or $a7,$t0,$t1
- beqz $a7,1f
-
- /* load the destination */
- lw $a7,-8($a0)
-
- pixel $a7,$t0,$a5,0
- andi $a5, 0xFFFF
- pixel $a7,$t1,$a5,16
- sw $a5,-8($a0)
-
-1:
- /* 2nd pair of pixels */
- lw $t0,-8($a1)
- lw $t1,-4($a1)
-
- /* both are zero, skip this pair */
- or $a7,$t0,$t1
- beqz $a7,1f
-
- /* load the destination */
- lw $a7,-4($a0)
-
- pixel $a7,$t0,$a5,0
- andi $a5, 0xFFFF
- pixel $a7,$t1,$a5,16
- sw $a5,-4($a0)
-
-1: subu $a2,4
- bgtz $a2,fourpixels
-
-tail:
- /* the pixel count underran, restore it now */
- addu $a2,4
-
- /* handle the last 0..3 pixels */
- beqz $a2,done
-onepixel:
- lw $t0,($a1)
- daddu $a0,2
- daddu $a1,4
- beqz $t0,1f
- lhu $a7,-2($a0)
- pixel $a7,$t0,$a5,0
- sh $a5,-2($a0)
-1: subu $a2,1
- bnez $a2,onepixel
-done:
-DBG .set push
-DBG .set mips32r2
-DBG rdhwr $a0,$3
-DBG mul $v0,$a0
-DBG mul $v1,$a0
-DBG .set pop
- daddiu $sp, $sp, 40
- j $ra
- .end scanline_t32cb16blend_mips64
diff --git a/libpixelflinger/tests/arch-mips/Android.bp b/libpixelflinger/tests/arch-mips/Android.bp
deleted file mode 100644
index 2ca2721..0000000
--- a/libpixelflinger/tests/arch-mips/Android.bp
+++ /dev/null
@@ -1,11 +0,0 @@
-cc_defaults {
- name: "pixelflinger-tests-mips",
- defaults: ["pixelflinger-tests"],
-
- enabled: false,
- arch: {
- mips: {
- enabled: true,
- },
- },
-}
diff --git a/libpixelflinger/tests/arch-mips/col32cb16blend/Android.bp b/libpixelflinger/tests/arch-mips/col32cb16blend/Android.bp
deleted file mode 100644
index 45bfe29..0000000
--- a/libpixelflinger/tests/arch-mips/col32cb16blend/Android.bp
+++ /dev/null
@@ -1,6 +0,0 @@
-cc_test {
- name: "test-pixelflinger-mips-col32cb16blend",
- defaults: ["pixelflinger-tests-mips"],
-
- srcs: ["col32cb16blend_test.c"],
-}
diff --git a/libpixelflinger/tests/arch-mips/col32cb16blend/col32cb16blend_test.c b/libpixelflinger/tests/arch-mips/col32cb16blend/col32cb16blend_test.c
deleted file mode 100644
index dd0e60f..0000000
--- a/libpixelflinger/tests/arch-mips/col32cb16blend/col32cb16blend_test.c
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <assert.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <inttypes.h>
-
-
-#define ARGB_8888_MAX 0xFFFFFFFF
-#define ARGB_8888_MIN 0x00000000
-#define RGB_565_MAX 0xFFFF
-#define RGB_565_MIN 0x0000
-
-struct test_t
-{
- char name[256];
- uint32_t src_color;
- uint16_t dst_color;
- size_t count;
-};
-
-struct test_t tests[] =
-{
- {"Count 1, Src=Max, Dst=Min", ARGB_8888_MAX, RGB_565_MIN, 1},
- {"Count 2, Src=Min, Dst=Max", ARGB_8888_MIN, RGB_565_MAX, 2},
- {"Count 3, Src=Max, Dst=Max", ARGB_8888_MAX, RGB_565_MAX, 3},
- {"Count 4, Src=Min, Dst=Min", ARGB_8888_MAX, RGB_565_MAX, 4},
- {"Count 1, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 1},
- {"Count 2, Src=Rand, Dst=Rand", 0xABCDEF12, 0x2345, 2},
- {"Count 3, Src=Rand, Dst=Rand", 0x11111111, 0xEDFE, 3},
- {"Count 4, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 4},
- {"Count 5, Src=Rand, Dst=Rand", 0xEFEFFEFE, 0xFACC, 5},
- {"Count 10, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 10}
-};
-
-void scanline_col32cb16blend_mips(uint16_t *dst, uint32_t src, size_t count);
-void scanline_col32cb16blend_c(uint16_t * dst, uint32_t src, size_t count)
-{
- uint32_t srcAlpha = (src>>24);
- uint32_t f = 0x100 - (srcAlpha + (srcAlpha>>7));
-
- while (count--)
- {
- uint16_t d = *dst;
- int dstR = (d>>11)&0x1f;
- int dstG = (d>>5)&0x3f;
- int dstB = (d)&0x1f;
- int srcR = (src >> ( 3))&0x1F;
- int srcG = (src >> ( 8+2))&0x3F;
- int srcB = (src >> (16+3))&0x1F;
- srcR += (f*dstR)>>8;
- srcG += (f*dstG)>>8;
- srcB += (f*dstB)>>8;
- *dst++ = (uint16_t)((srcR<<11)|(srcG<<5)|srcB);
- }
-}
-
-void scanline_col32cb16blend_test()
-{
- uint16_t dst_c[16], dst_asm[16];
- uint32_t i, j;
-
- for(i = 0; i < sizeof(tests)/sizeof(struct test_t); ++i)
- {
- struct test_t test = tests[i];
-
- printf("Testing - %s:",test.name);
-
- memset(dst_c, 0, sizeof(dst_c));
- memset(dst_asm, 0, sizeof(dst_asm));
-
- for(j = 0; j < test.count; ++j)
- {
- dst_c[j] = test.dst_color;
- dst_asm[j] = test.dst_color;
- }
-
-
- scanline_col32cb16blend_c(dst_c, test.src_color, test.count);
- scanline_col32cb16blend_mips(dst_asm, test.src_color, test.count);
-
- if(memcmp(dst_c, dst_asm, sizeof(dst_c)) == 0)
- printf("Passed\n");
- else
- printf("Failed\n");
-
- for(j = 0; j < test.count; ++j)
- {
- printf("dst_c[%d] = %x, dst_asm[%d] = %x \n", j, dst_c[j], j, dst_asm[j]);
- }
- }
-}
-
-int main()
-{
- scanline_col32cb16blend_test();
- return 0;
-}
diff --git a/libpixelflinger/tests/arch-mips/t32cb16blend/Android.bp b/libpixelflinger/tests/arch-mips/t32cb16blend/Android.bp
deleted file mode 100644
index 069e97c..0000000
--- a/libpixelflinger/tests/arch-mips/t32cb16blend/Android.bp
+++ /dev/null
@@ -1,6 +0,0 @@
-cc_test {
- name: "test-pixelflinger-mips-t32cb16blend",
- defaults: ["pixelflinger-tests-mips"],
-
- srcs: ["t32cb16blend_test.c"],
-}
diff --git a/libpixelflinger/tests/arch-mips/t32cb16blend/t32cb16blend_test.c b/libpixelflinger/tests/arch-mips/t32cb16blend/t32cb16blend_test.c
deleted file mode 100644
index c6d6937..0000000
--- a/libpixelflinger/tests/arch-mips/t32cb16blend/t32cb16blend_test.c
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <assert.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <inttypes.h>
-
-#define ARGB_8888_MAX 0xFFFFFFFF
-#define ARGB_8888_MIN 0x00000000
-#define RGB_565_MAX 0xFFFF
-#define RGB_565_MIN 0x0000
-
-struct test_t
-{
- char name[256];
- uint32_t src_color;
- uint16_t dst_color;
- size_t count;
-};
-
-struct test_t tests[] =
-{
- {"Count 0", 0, 0, 0},
- {"Count 1, Src=Max, Dst=Min", ARGB_8888_MAX, RGB_565_MIN, 1},
- {"Count 2, Src=Min, Dst=Max", ARGB_8888_MIN, RGB_565_MAX, 2},
- {"Count 3, Src=Max, Dst=Max", ARGB_8888_MAX, RGB_565_MAX, 3},
- {"Count 4, Src=Min, Dst=Min", ARGB_8888_MAX, RGB_565_MAX, 4},
- {"Count 1, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 1},
- {"Count 2, Src=Rand, Dst=Rand", 0xABCDEF12, 0x2345, 2},
- {"Count 3, Src=Rand, Dst=Rand", 0x11111111, 0xEDFE, 3},
- {"Count 4, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 4},
- {"Count 5, Src=Rand, Dst=Rand", 0xEFEFFEFE, 0xFACC, 5},
- {"Count 10, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 10}
-
-};
-
-void scanline_t32cb16blend_mips(uint16_t*, uint32_t*, size_t);
-void scanline_t32cb16blend_c(uint16_t * dst, uint32_t* src, size_t count)
-{
- while (count--)
- {
- uint16_t d = *dst;
- uint32_t s = *src++;
- int dstR = (d>>11)&0x1f;
- int dstG = (d>>5)&0x3f;
- int dstB = (d)&0x1f;
- int srcR = (s >> ( 3))&0x1F;
- int srcG = (s >> ( 8+2))&0x3F;
- int srcB = (s >> (16+3))&0x1F;
- int srcAlpha = (s>>24) & 0xFF;
-
-
- int f = 0x100 - (srcAlpha + ((srcAlpha>>7) & 0x1));
- srcR += (f*dstR)>>8;
- srcG += (f*dstG)>>8;
- srcB += (f*dstB)>>8;
- // srcR = srcR > 0x1F? 0x1F: srcR;
- // srcG = srcG > 0x3F? 0x3F: srcG;
- // srcB = srcB > 0x1F? 0x1F: srcB;
- *dst++ = (uint16_t)((srcR<<11)|(srcG<<5)|srcB);
- }
-}
-
-void scanline_t32cb16blend_test()
-{
- uint16_t dst_c[16], dst_asm[16];
- uint32_t src[16];
- uint32_t i;
- uint32_t j;
-
- for(i = 0; i < sizeof(tests)/sizeof(struct test_t); ++i)
- {
- struct test_t test = tests[i];
-
- printf("Testing - %s:",test.name);
-
- memset(dst_c, 0, sizeof(dst_c));
- memset(dst_asm, 0, sizeof(dst_asm));
-
- for(j = 0; j < test.count; ++j)
- {
- dst_c[j] = test.dst_color;
- dst_asm[j] = test.dst_color;
- src[j] = test.src_color;
- }
-
- scanline_t32cb16blend_c(dst_c,src,test.count);
- scanline_t32cb16blend_mips(dst_asm,src,test.count);
-
-
- if(memcmp(dst_c, dst_asm, sizeof(dst_c)) == 0)
- printf("Passed\n");
- else
- printf("Failed\n");
-
- for(j = 0; j < test.count; ++j)
- {
- printf("dst_c[%d] = %x, dst_asm[%d] = %x \n", j, dst_c[j], j, dst_asm[j]);
- }
- }
-}
-
-int main()
-{
- scanline_t32cb16blend_test();
- return 0;
-}
diff --git a/libpixelflinger/tests/arch-mips64/Android.bp b/libpixelflinger/tests/arch-mips64/Android.bp
deleted file mode 100644
index ba55d62..0000000
--- a/libpixelflinger/tests/arch-mips64/Android.bp
+++ /dev/null
@@ -1,11 +0,0 @@
-cc_defaults {
- name: "pixelflinger-tests-mips64",
- defaults: ["pixelflinger-tests"],
-
- enabled: false,
- arch: {
- mips64: {
- enabled: true,
- },
- },
-}
diff --git a/libpixelflinger/tests/arch-mips64/assembler/Android.bp b/libpixelflinger/tests/arch-mips64/assembler/Android.bp
deleted file mode 100644
index b672053..0000000
--- a/libpixelflinger/tests/arch-mips64/assembler/Android.bp
+++ /dev/null
@@ -1,9 +0,0 @@
-cc_test {
- name: "test-pixelflinger-mips64-assembler-test",
- defaults: ["pixelflinger-tests-mips64"],
-
- srcs: [
- "mips64_assembler_test.cpp",
- "asm_mips_test_jacket.S",
- ],
-}
diff --git a/libpixelflinger/tests/arch-mips64/assembler/asm_mips_test_jacket.S b/libpixelflinger/tests/arch-mips64/assembler/asm_mips_test_jacket.S
deleted file mode 100644
index 705ee9b..0000000
--- a/libpixelflinger/tests/arch-mips64/assembler/asm_mips_test_jacket.S
+++ /dev/null
@@ -1,93 +0,0 @@
-# /*
-# * Copyright (C) 2015 The Android Open Source Project
-# * All rights reserved.
-# *
-# * Redistribution and use in source and binary forms, with or without
-# * modification, are permitted provided that the following conditions
-# * are met:
-# * * Redistributions of source code must retain the above copyright
-# * notice, this list of conditions and the following disclaimer.
-# * * Redistributions in binary form must reproduce the above copyright
-# * notice, this list of conditions and the following disclaimer in
-# * the documentation and/or other materials provided with the
-# * distribution.
-# *
-# * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-# * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-# * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-# * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-# * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-# * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-# * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-# * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-# * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-# * SUCH DAMAGE.
-# */
-
- .text
- .balign 8
-
- .global asm_mips_test_jacket
-
- # // Set the register
- # // Calls the asm function
- # // Reads the register values to output register
-
- # // Parameters
- # // a0 - Function to jump
- # // a1 - register values array
- # // a2 - flag values array
-asm_mips_test_jacket:
- # // Save registers to stack
- daddiu $sp, $sp, -96
- sd $s0, 64($sp)
- sd $s1, 72($sp)
- sd $s2, 80($sp)
- sd $ra, 88($sp)
-
- move $s0, $a0
- move $s1, $a1
- move $s2, $a2
-
- ld $v0, 16($s1)
- ld $v1, 24($s1)
- ld $a0, 32($s1)
- ld $a1, 40($s1)
- ld $a2, 48($s1)
- ld $a3, 56($s1)
- ld $a4, 64($s1)
- ld $a5, 72($s1)
- ld $a6, 80($s1)
- ld $a7, 88($s1)
- ld $t0, 96($s1)
- ld $t1, 104($s1)
- ld $t2, 112($s1)
- ld $t3, 120($s1)
-
- jal $s0
-
- sd $v0, 16($s1)
- sd $v1, 24($s1)
- sd $a0, 32($s1)
- sd $a1, 40($s1)
- sd $a2, 48($s1)
- sd $a3, 56($s1)
- sd $a4, 64($s1)
- sd $a5, 72($s1)
- sd $a6, 80($s1)
- sd $a7, 88($s1)
- sd $t0, 96($s1)
- sd $t1, 104($s1)
- sd $t2, 112($s1)
- sd $t3, 120($s1)
-
- ld $s0, 64($sp)
- ld $s1, 72($sp)
- ld $s2, 80($sp)
- ld $ra, 88($sp)
-
- daddiu $sp, $sp, 96
-
- j $ra
diff --git a/libpixelflinger/tests/arch-mips64/assembler/mips64_assembler_test.cpp b/libpixelflinger/tests/arch-mips64/assembler/mips64_assembler_test.cpp
deleted file mode 100644
index 9fb0a28..0000000
--- a/libpixelflinger/tests/arch-mips64/assembler/mips64_assembler_test.cpp
+++ /dev/null
@@ -1,643 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <errno.h>
-#define __STDC_FORMAT_MACROS
-#include <inttypes.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <unistd.h>
-
-#include <android/log.h>
-#include <cutils/ashmem.h>
-
-#include "codeflinger/ARMAssemblerInterface.h"
-#include "codeflinger/MIPS64Assembler.h"
-
-using namespace android;
-
-#define TESTS_DATAOP_ENABLE 1
-#define TESTS_DATATRANSFER_ENABLE 1
-#define ASSEMBLY_SCRATCH_SIZE 4096
-
-void *instrMem;
-uint32_t instrMemSize = 128 * 1024;
-char dataMem[8192];
-
-typedef void (*asm_function_t)();
-extern "C" void asm_mips_test_jacket(asm_function_t function,
- int64_t regs[], int32_t flags[]);
-
-#define MAX_32BIT (uint32_t)(((uint64_t)1 << 32) - 1)
-#define MAX_64BIT ((uint64_t)0xFFFFFFFFFFFFFFFF)
-const uint32_t NA = 0;
-const uint32_t NUM_REGS = 32;
-const uint32_t NUM_FLAGS = 16;
-
-enum instr_t
-{
- INSTR_ADD,
- INSTR_SUB,
- INSTR_AND,
- INSTR_ORR,
- INSTR_RSB,
- INSTR_BIC,
- INSTR_CMP,
- INSTR_MOV,
- INSTR_MVN,
- INSTR_MUL,
- INSTR_MLA,
- INSTR_SMULBB,
- INSTR_SMULBT,
- INSTR_SMULTB,
- INSTR_SMULTT,
- INSTR_SMULWB,
- INSTR_SMULWT,
- INSTR_SMLABB,
- INSTR_UXTB16,
- INSTR_UBFX,
- INSTR_ADDR_ADD,
- INSTR_ADDR_SUB,
- INSTR_LDR,
- INSTR_LDRB,
- INSTR_LDRH,
- INSTR_ADDR_LDR,
- INSTR_LDM,
- INSTR_STR,
- INSTR_STRB,
- INSTR_STRH,
- INSTR_ADDR_STR,
- INSTR_STM
-};
-
-enum shift_t
-{
- SHIFT_LSL,
- SHIFT_LSR,
- SHIFT_ASR,
- SHIFT_ROR,
- SHIFT_NONE
-};
-
-enum offset_t
-{
- REG_SCALE_OFFSET,
- REG_OFFSET,
- IMM8_OFFSET,
- IMM12_OFFSET,
- NO_OFFSET
-};
-
-enum cond_t
-{
- EQ, NE, CS, CC, MI, PL, VS, VC, HI, LS, GE, LT, GT, LE, AL, NV,
- HS = CS,
- LO = CC
-};
-
-const char * cc_code[] =
-{
- "EQ", "NE", "CS", "CC", "MI", "PL", "VS", "VC",
- "HI", "LS","GE","LT", "GT", "LE", "AL", "NV"
-};
-
-struct condTest_t
-{
- int mode;
- int32_t Rcond1;
- int32_t Rcond2;
- uint64_t Rcond1Value;
- uint64_t Rcond2Value;
-};
-
-
-struct dataOpTest_t
-{
- uint32_t id;
- instr_t op;
- condTest_t preCond;
- cond_t cond;
- bool setFlags;
- uint64_t RnValue;
- uint64_t RsValue;
- bool immediate;
- uint32_t immValue;
- uint64_t RmValue;
- uint32_t shiftMode;
- uint32_t shiftAmount;
- uint64_t RdValue;
- bool checkRd;
- uint64_t postRdValue;
-};
-
-struct dataTransferTest_t
-{
- uint32_t id;
- instr_t op;
- uint32_t preFlag;
- cond_t cond;
- bool setMem;
- uint64_t memOffset;
- uint64_t memValue;
- uint64_t RnValue;
- offset_t offsetType;
- uint64_t RmValue;
- uint32_t immValue;
- bool writeBack;
- bool preIndex;
- bool postIndex;
- uint64_t RdValue;
- uint64_t postRdValue;
- uint64_t postRnValue;
- bool checkMem;
- uint64_t postMemOffset;
- uint32_t postMemLength;
- uint64_t postMemValue;
-};
-
-
-dataOpTest_t dataOpTests [] =
-{
- {0xA000,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,1,MAX_32BIT,NA,NA,NA,NA,1,0},
- {0xA001,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,1,MAX_32BIT-1,NA,NA,NA,NA,1,MAX_64BIT},
- {0xA002,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,0,NA,MAX_32BIT,NA,NA,NA,1,0},
- {0xA003,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,0,NA,MAX_32BIT-1,NA,NA,NA,1,MAX_64BIT},
- {0xA004,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSL, 0,NA,1,0},
- {0xA005,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSL,31,NA,1,0xFFFFFFFF80000001},
- {0xA006,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,0,0,3,SHIFT_LSR,1,NA,1,2},
- {0xA007,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSR,31,NA,1,2},
- {0xA008,INSTR_ADD,{0,0,0,0,0},AL,0,0,NA,0,0,3,SHIFT_ASR,1,NA,1,1},
- {0xA009,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_64BIT,SHIFT_ASR,31,NA,1,0},
- {0xA010,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,1,MAX_32BIT,0,0,0,NA,1,1},
- {0xA011,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,1,MAX_32BIT-1,0,0,0,NA,1,0},
- {0xA012,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,0,0,NA,1,1},
- {0xA013,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT-1,0,0,NA,1,0},
- {0xA014,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSL,0,NA,1,1},
- {0xA015,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSL,31,NA,1,0},
- {0xA016,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,0,0,3,SHIFT_LSR,1,NA,1,1},
- {0xA017,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSR,31,NA,1,1},
- {0xA018,INSTR_AND,{0,0,0,0,0},AL,0,0,NA,0,0,3,SHIFT_ASR,1,NA,1,0},
- {0xA019,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_ASR,31,NA,1,1},
- {0xA020,INSTR_ORR,{0,0,0,0,0},AL,0,3,NA,1,MAX_32BIT,0,0,0,NA,1,MAX_64BIT},
- {0xA021,INSTR_ORR,{0,0,0,0,0},AL,0,2,NA,1,MAX_32BIT-1,0,0,0,NA,1,MAX_64BIT-1},
- {0xA022,INSTR_ORR,{0,0,0,0,0},AL,0,3,NA,0,0,MAX_32BIT,0,0,NA,1,MAX_64BIT},
- {0xA023,INSTR_ORR,{0,0,0,0,0},AL,0,2,NA,0,0,MAX_32BIT-1,0,0,NA,1,MAX_64BIT-1},
- {0xA024,INSTR_ORR,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSL,0,NA,1,MAX_64BIT},
- {0xA025,INSTR_ORR,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSL,31,NA,1,0xFFFFFFFF80000001},
- {0xA026,INSTR_ORR,{0,0,0,0,0},AL,0,1,NA,0,0,3,SHIFT_LSR,1,NA,1,1},
- {0xA027,INSTR_ORR,{0,0,0,0,0},AL,0,0,NA,0,0,MAX_32BIT,SHIFT_LSR,31,NA,1,1},
- {0xA028,INSTR_ORR,{0,0,0,0,0},AL,0,0,NA,0,0,3,SHIFT_ASR,1,NA,1,1},
- {0xA029,INSTR_ORR,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_64BIT,SHIFT_ASR,31,NA,1,MAX_64BIT},
- {0xA030,INSTR_CMP,{0,0,0,0,0},AL,1,0x10000,NA,1,0x10000,0,0,0,NA,0,0},
- {0xA031,INSTR_MUL,{0,0,0,0,0},AL,0,0,0x10000,0,0,0x10000,0,0,NA,1,0},
- {0xA032,INSTR_MUL,{0,0,0,0,0},AL,0,0,0x1000,0,0,0x10000,0,0,NA,1,0x10000000},
- {0xA033,INSTR_MUL,{0,0,0,0,0},AL,0,0,MAX_32BIT,0,0,1,0,0,NA,1,MAX_64BIT},
- {0xA034,INSTR_MLA,{0,0,0,0,0},AL,0,0x10000,0x10000,0,0,0x10000,0,0,NA,1,0x10000},
- {0xA035,INSTR_MLA,{0,0,0,0,0},AL,0,0x10000,0x1000,0,0,0x10000,0,0,NA,1,0x10010000},
- {0xA036,INSTR_SUB,{1,R_v1,R_a6,2,4},MI,0,2,NA,0,NA,1,NA,NA,2,1,1},
- {0xA037,INSTR_SUB,{2,R_v1,R_a6,2,0},MI,0,2,NA,0,NA,1,NA,NA,2,1,2},
- {0xA038,INSTR_SUB,{1,R_v1,R_a6,4,2},GE,0,2,NA,1,1,NA,NA,NA,2,1,1},
- {0xA039,INSTR_SUB,{1,R_a5,R_a6,2,7},GE,0,2,NA,1,1,NA,NA,NA,2,1,2},
- {0xA040,INSTR_SUB,{1,R_a5,R_a6,1,1},HS,0,2,NA,1,1,NA,NA,NA,2,1,1},
- {0xA041,INSTR_SUB,{1,R_a5,R_a6,0,1},HS,0,2,NA,1,1,NA,NA,NA,2,1,2},
- {0xA042,INSTR_SUB,{0,0,0,0,0},AL,0,1,NA,1,1<< 16,0,0,0,NA,1,UINT64_C(1) -(1<<16)},
- {0xA043,INSTR_SUB,{0,0,0,0,0},AL,0,MAX_32BIT,NA,1,1,0,0,0,NA,1,MAX_64BIT-1},
- {0xA044,INSTR_SUB,{0,0,0,0,0},AL,0,1,NA,1,1,0,0,0,NA,1,0},
- {0xA045,INSTR_SUB,{0,0,0,0,0},AL,0,1,NA,0,NA,1<<16,0,0,NA,1,UINT64_C(1) -(1<<16)},
- {0xA046,INSTR_SUB,{0,0,0,0,0},AL,0,MAX_32BIT,NA,0,NA,1,0,0,NA,1,MAX_64BIT-1},
- {0xA047,INSTR_SUB,{0,0,0,0,0},AL,0,1,NA,0,NA,1,0,0,NA,1,0},
- {0xA048,INSTR_SUB,{0,0,0,0,0},AL,0,1,NA,0,NA,1,SHIFT_LSL,16,NA,1,UINT64_C(1) -(1<<16)},
- {0xA049,INSTR_SUB,{0,0,0,0,0},AL,0,0x80000001,NA,0,NA,MAX_32BIT,SHIFT_LSL,31,NA,1,1},
- {0xA050,INSTR_SUB,{0,0,0,0,0},AL,0,1,NA,0,NA,3,SHIFT_LSR,1,NA,1,0},
- {0xA051,INSTR_SUB,{0,0,0,0,0},AL,0,1,NA,0,NA,MAX_32BIT,SHIFT_LSR,31,NA,1,0},
- {0xA052,INSTR_RSB,{1,R_a5,R_a6,4,1},GE,0,2,NA,1,0,NA,NA,NA,2,1,UINT64_C(-2)},
- {0xA053,INSTR_RSB,{1,R_a5,R_a6,UINT64_C(-1),1},GE,0,2,NA,1,0,NA,NA,NA,2,1,2},
- {0xA054,INSTR_RSB,{0,0,0,0,0},AL,0,1,NA,1,1<<16,NA,NA,NA,NA,1,(1<<16)-1},
- {0xA055,INSTR_RSB,{0,0,0,0,0},AL,0,MAX_32BIT,NA,1,1,NA,NA,NA,NA,1,UINT64_C(1)-MAX_64BIT},
- {0xA056,INSTR_RSB,{0,0,0,0,0},AL,0,1,NA,1,1,NA,NA,NA,NA,1,0},
- {0xA057,INSTR_RSB,{0,0,0,0,0},AL,0,1,NA,0,NA,1<<16,0,0,NA,1,(1<<16)-1},
- {0xA058,INSTR_RSB,{0,0,0,0,0},AL,0,MAX_32BIT,NA,0,NA,1,0,0,NA,1,UINT64_C(1)-MAX_64BIT},
- {0xA059,INSTR_RSB,{0,0,0,0,0},AL,0,1,NA,0,NA,1,0,0,NA,1,0},
- {0xA060,INSTR_RSB,{0,0,0,0,0},AL,0,1,NA,0,NA,1,SHIFT_LSL,16,NA,1,(1<<16)-1},
- {0xA061,INSTR_RSB,{0,0,0,0,0},AL,0,0x80000001,NA,0,NA,MAX_32BIT ,SHIFT_LSL,31,NA,1,UINT64_C(-1)},
- {0xA062,INSTR_RSB,{0,0,0,0,0},AL,0,1,NA,0,NA,3,SHIFT_LSR,1,NA,1,0},
- {0xA063,INSTR_RSB,{0,0,0,0,0},AL,0,1,NA,0,NA,MAX_32BIT,SHIFT_LSR,31,NA,1,0},
- {0xA064,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,1,0x80000001,NA,NA,NA,NA,1,0xFFFFFFFF80000001},
- {0xA065,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,0x80000001,0,0,NA,1,0xFFFFFFFF80000001},
- {0xA066,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,1,NA,1,MAX_64BIT-1},
- {0xA067,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,31,NA,1,0xFFFFFFFF80000000},
- {0xA068,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,3,SHIFT_LSR,1,NA,1,1},
- {0xA069,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSR,31,NA,1,1},
- {0xA070,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,3,SHIFT_ASR,1,NA,1,1},
- {0xA071,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,MAX_64BIT ,SHIFT_ASR,31,NA,1,MAX_64BIT},
- {0xA072,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,3,SHIFT_ROR,1,NA,1,0xFFFFFFFF80000001},
- {0xA073,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,0x80000001,SHIFT_ROR,31,NA,1,3},
- {0xA074,INSTR_MOV,{0,0,0,0,0},AL,1,NA,NA,0,0,MAX_64BIT -1,SHIFT_ASR,1,NA,1,MAX_64BIT},
- {0xA075,INSTR_MOV,{0,0,0,0,0},AL,1,NA,NA,0,0,3,SHIFT_ASR,1,NA,1,1},
- {0xA076,INSTR_MOV,{2,R_a5,R_a6,6,8},MI,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2},
- {0xA077,INSTR_MOV,{2,R_a5,R_a6,UINT64_C(-4),UINT64_C(-8)},MI,0,NA,NA,0,0,0x80000001,0,0,2,1,0xFFFFFFFF80000001},
- {0xA078,INSTR_MOV,{1,R_a5,R_a6,UINT64_C(-1),UINT64_C(-1)},LT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2},
- {0xA079,INSTR_MOV,{1,R_a5,R_a6,UINT64_C(-1),1},LT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,0xFFFFFFFF80000001},
- {0xA080,INSTR_MOV,{1,R_a5,R_a6,UINT64_C(-1),UINT64_C(-5)},GE,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,1,2,1,MAX_64BIT-1},
- {0xA081,INSTR_MOV,{1,R_a5,R_a6,5,5},GE,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,31,2,1,0xFFFFFFFF80000000},
- {0xA082,INSTR_MOV,{1,R_a5,R_a6,UINT64_C(-1),1},GE,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,31,2,1,2},
- {0xA083,INSTR_MOV,{1,R_a5,R_a6,4,1},LE,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,1,2,1,2},
- {0xA084,INSTR_MOV,{1,R_a5,R_a6,UINT64_C(-1),UINT64_C(-1)},LE,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,0xFFFFFFFF80000001},
- {0xA085,INSTR_MOV,{1,R_a5,R_a6,UINT64_C(-1),1},LE,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,31,2,1,0xFFFFFFFF80000000},
- {0xA086,INSTR_MOV,{1,R_a5,R_a6,1,1},GT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2},
- {0xA087,INSTR_MOV,{1,R_a5,R_a6,UINT64_C(-1),UINT64_C(-3)},GT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,0xFFFFFFFF80000001},
- {0xA088,INSTR_MOV,{1,R_a5,R_a6,UINT64_C(-1),0},GT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2},
- {0xA089,INSTR_MOV,{1,R_a5,R_a6,UINT64_C(-1),UINT64_C(-1)},GT,0,NA,NA,0,0,0x80000001,0,0,2,1,2},
- {0xA090,INSTR_MOV,{1,R_a5,R_a6,6,1},GT,0,NA,NA,0,0,0x80000001,0,0,2,1,0xFFFFFFFF80000001},
- {0xA091,INSTR_MOV,{1,R_a5,R_a6,UINT64_C(-1),1},GT,0,NA,NA,0,0,0x80000001,0,0,2,1,2},
- {0xA092,INSTR_MOV,{1,R_a5,R_a6,1,1},GT,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,1,2,1,2},
- {0xA093,INSTR_MOV,{1,R_a5,R_a6,4,1},GT,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,1,2,1,MAX_64BIT-1},
- {0xA094,INSTR_MOV,{1,R_a5,R_a6,UINT64_C(-1),1},GT,0,NA,NA,0,0,MAX_32BIT ,SHIFT_LSL,1,2,1,2},
- {0xA095,INSTR_MOV,{1,R_a5,R_a6,1,UINT64_C(-1)},HS,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2},
- {0xA096,INSTR_MOV,{1,R_a5,R_a6,UINT64_C(-1),1},HS,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,0xFFFFFFFF80000001},
- {0xA097,INSTR_MVN,{1,R_a5,R_a6,1,4},HS,0,NA,NA,1,MAX_32BIT-1,NA,NA,NA,2,1,2},
- {0xA098,INSTR_MVN,{1,R_a5,R_a6,UINT64_C(-1),1},HS,0,NA,NA,1,MAX_32BIT-1,NA,NA,NA,2,1,1},
- {0xA099,INSTR_MVN,{0,0,0,0,0},AL,0,NA,NA,1,0,NA,NA,NA,2,1,MAX_64BIT},
- {0xA100,INSTR_MVN,{0,0,0,0,0},AL,0,NA,NA,0,NA,MAX_32BIT-1,NA,0,2,1,1},
- {0xA101,INSTR_MVN,{0,0,0,0,0},AL,0,NA,NA,0,NA,0x80000001,NA,0,2,1,0x7FFFFFFE},
- {0xA102,INSTR_BIC,{0,0,0,0,0},AL,0,1,NA,1,MAX_32BIT,NA,NA,NA,NA,1,0},
- {0xA103,INSTR_BIC,{0,0,0,0,0},AL,0,1,NA,1,MAX_32BIT-1,NA,NA,NA,NA,1,1},
- {0xA104,INSTR_BIC,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,0,0,NA,1,0},
- {0xA105,INSTR_BIC,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT-1,0,0,NA,1,1},
- {0xA106,INSTR_BIC,{0,0,0,0,0},AL,0,0xF0,NA,0,0,3,SHIFT_ASR,1,NA,1,0xF0},
- {0xA107,INSTR_BIC,{0,0,0,0,0},AL,0,0xF0,NA,0,0,MAX_64BIT,SHIFT_ASR,31,NA,1,0},
- {0xA108,INSTR_SMULBB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCDFFFF,0,NA,0xFFFFFFFFABCD0001,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
- {0xA109,INSTR_SMULBB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCD0001,0,NA,0xFFFFFFFFABCD0FFF,NA,NA,NA,1,0x00000FFF},
- {0xA110,INSTR_SMULBB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCD0001,0,NA,0xFFFFFFFFABCDFFFF,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
- {0xA111,INSTR_SMULBB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCDFFFF,0,NA,0xFFFFFFFFABCDFFFF,NA,NA,NA,1,1},
- {0xA112,INSTR_SMULBT,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFFFFFABCD,0,NA,0xFFFFFFFFABCD0001,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
- {0xA113,INSTR_SMULBT,{0,0,0,0,0},AL,0,NA,0x000000000001ABCD,0,NA,0xFFFFFFFFABCD0FFF,NA,NA,NA,1,0x00000FFF},
- {0xA114,INSTR_SMULBT,{0,0,0,0,0},AL,0,NA,0x000000000001ABCD,0,NA,0xFFFFFFFFABCDFFFF,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
- {0xA115,INSTR_SMULBT,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFFFFFABCD,0,NA,0xFFFFFFFFABCDFFFF,NA,NA,NA,1,1},
- {0xA116,INSTR_SMULTB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCDFFFF,0,NA,0x000000000001ABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
- {0xA117,INSTR_SMULTB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCD0001,0,NA,0x000000000FFFABCD,NA,NA,NA,1,0x00000FFF},
- {0xA118,INSTR_SMULTB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCD0001,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
- {0xA119,INSTR_SMULTB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCDFFFF,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,1},
- {0xA120,INSTR_SMULTT,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFFFFFABCD,0,NA,0x000000000001ABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
- {0xA121,INSTR_SMULTT,{0,0,0,0,0},AL,0,NA,0x000000000001ABCD,0,NA,0x000000000FFFABCD,NA,NA,NA,1,0x00000FFF},
- {0xA122,INSTR_SMULTT,{0,0,0,0,0},AL,0,NA,0x000000000001ABCD,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
- {0xA123,INSTR_SMULTT,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFFFFFABCD,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,1},
- {0xA124,INSTR_SMULWB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCDFFFF,0,NA,0x000000000001ABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFE},
- {0xA125,INSTR_SMULWB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCD0001,0,NA,0x000000000FFFABCD,NA,NA,NA,1,0x00000FFF},
- {0xA126,INSTR_SMULWB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCD0001,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
- {0xA127,INSTR_SMULWB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCDFFFF,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,0},
- {0xA128,INSTR_SMULWT,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFFFFFABCD,0,NA,0x000000000001ABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFE},
- {0xA129,INSTR_SMULWT,{0,0,0,0,0},AL,0,NA,0x000000000001ABCD,0,NA,0x000000000FFFABCD,NA,NA,NA,1,0x00000FFF},
- {0xA130,INSTR_SMULWT,{0,0,0,0,0},AL,0,NA,0x000000000001ABCD,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
- {0xA131,INSTR_SMULWT,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFFFFFABCD,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,0},
- {0xA132,INSTR_SMLABB,{0,0,0,0,0},AL,0,1,0xFFFFFFFFABCDFFFF,0,NA,0xFFFFFFFFABCD0001,NA,NA,NA,1,0},
- {0xA133,INSTR_SMLABB,{0,0,0,0,0},AL,0,1,0xFFFFFFFFABCD0001,0,NA,0xFFFFFFFFABCD0FFF,NA,NA,NA,1,0x00001000},
- {0xA134,INSTR_SMLABB,{0,0,0,0,0},AL,0,0xFFFFFFFFFFFFFFFF,0xFFFFFFFFABCD0001,0,NA,0xABCDFFFF,NA,NA,NA,1,0xFFFFFFFFFFFFFFFE},
- {0xA135,INSTR_SMLABB,{0,0,0,0,0},AL,0,0xFFFFFFFFFFFFFFFF,0xFFFFFFFFABCDFFFF,0,NA,0xABCDFFFF,NA,NA,NA,1,0},
- {0xA136,INSTR_UXTB16,{0,0,0,0,0},AL,0,NA,NA,0,NA,0xABCDEF01,SHIFT_ROR,0,NA,1,0x00CD0001},
- {0xA137,INSTR_UXTB16,{0,0,0,0,0},AL,0,NA,NA,0,NA,0xABCDEF01,SHIFT_ROR,1,NA,1,0x00AB00EF},
- {0xA138,INSTR_UXTB16,{0,0,0,0,0},AL,0,NA,NA,0,NA,0xABCDEF01,SHIFT_ROR,2,NA,1,0x000100CD},
- {0xA139,INSTR_UXTB16,{0,0,0,0,0},AL,0,NA,NA,0,NA,0xABCDEF01,SHIFT_ROR,3,NA,1,0x00EF00AB},
- {0xA140,INSTR_ADDR_ADD,{0,0,0,0,0},AL,0,0xCFFFFFFFF,NA,0,NA,0x1,SHIFT_LSL,1,NA,1,0xD00000001},
- {0xA141,INSTR_ADDR_ADD,{0,0,0,0,0},AL,0,0x01,NA,0,NA,0x1,SHIFT_LSL,2,NA,1,0x5},
- {0xA142,INSTR_ADDR_ADD,{0,0,0,0,0},AL,0,0xCFFFFFFFF,NA,0,NA,0x1,NA,0,NA,1,0xD00000000},
- {0xA143,INSTR_ADDR_SUB,{0,0,0,0,0},AL,0,0xD00000001,NA,0,NA,0x010000,SHIFT_LSR,15,NA,1,0xCFFFFFFFF},
- {0xA144,INSTR_ADDR_SUB,{0,0,0,0,0},AL,0,0xCFFFFFFFF,NA,0,NA,0x020000,SHIFT_LSR,15,NA,1,0xCFFFFFFFB},
- {0xA145,INSTR_ADDR_SUB,{0,0,0,0,0},AL,0,3,NA,0,NA,0x010000,SHIFT_LSR,15,NA,1,1},
-};
-
-dataTransferTest_t dataTransferTests [] =
-{
- {0xB000,INSTR_LDR,AL,AL,1,24,0xABCDEF0123456789,0,REG_SCALE_OFFSET,24,NA,NA,NA,NA,NA,0x23456789,0,0,NA,NA,NA},
- {0xB001,INSTR_LDR,AL,AL,1,0,0xABCDEF0123456789,0,IMM12_OFFSET,NA,4,1,0,1,NA,0x23456789,4,0,NA,NA,NA},
- {0xB002,INSTR_LDR,AL,AL,1,0,0xABCDEF0123456789,0,NO_OFFSET,NA,NA,0,0,0,NA,0x23456789,0,0,NA,NA,NA},
- {0xB003,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,0,REG_SCALE_OFFSET,4064,NA,NA,NA,NA,NA,0x89,0,0,NA,NA,NA},
- {0xB004,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,4065,IMM12_OFFSET,NA,0,0,1,0,NA,0x67,4065,0,NA,NA,NA},
- {0xB005,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,4065,IMM12_OFFSET,NA,1,0,1,0,NA,0x45,4065,0,NA,NA,NA},
- {0xB006,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,4065,IMM12_OFFSET,NA,2,0,1,0,NA,0x23,4065,0,NA,NA,NA},
- {0xB007,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,4065,IMM12_OFFSET,NA,1,1,0,1,NA,0x67,4066,0,NA,NA,NA},
- {0xB008,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,0,NO_OFFSET,NA,NA,0,0,0,NA,0x89,0,0,NA,NA,NA},
- {0xB009,INSTR_LDRH,AL,AL,1,0,0xABCDEF0123456789,0,IMM8_OFFSET,NA,2,1,0,1,NA,0x6789,2,0,NA,NA,NA},
- {0xB010,INSTR_LDRH,AL,AL,1,4064,0xABCDEF0123456789,0,REG_OFFSET,4064,0,0,1,0,NA,0x6789,0,0,NA,NA,NA},
- {0xB011,INSTR_LDRH,AL,AL,1,4064,0xABCDEF0123456789,0,REG_OFFSET,4066,0,0,1,0,NA,0x2345,0,0,NA,NA,NA},
- {0xB012,INSTR_LDRH,AL,AL,1,0,0xABCDEF0123456789,0,NO_OFFSET,NA,0,0,0,0,NA,0x6789,0,0,NA,NA,NA},
- {0xB013,INSTR_LDRH,AL,AL,1,0,0xABCDEF0123456789,2,NO_OFFSET,NA,0,0,0,0,NA,0x2345,2,0,NA,NA,NA},
- {0xB014,INSTR_STR,AL,AL,1,2,0xDEADBEEFDEADBEEF,4,IMM12_OFFSET,NA,4,1,0,1,0xABCDEF0123456789,0xABCDEF0123456789,8,1,2,8,0xDEAD23456789BEEF},
- {0xB015,INSTR_STR,AL,AL,1,2,0xDEADBEEFDEADBEEF,4,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4,1,2,8,0xDEAD23456789BEEF},
- {0xB016,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,IMM12_OFFSET,NA,0,0,1,0,0xABCDEF0123456789,0xABCDEF0123456789,1,1,0,8,0xDEADBEEFDEAD89EF},
- {0xB017,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,IMM12_OFFSET,NA,1,0,1,0,0xABCDEF0123456789,0xABCDEF0123456789,1,1,0,8,0xDEADBEEFDE89BEEF},
- {0xB018,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,IMM12_OFFSET,NA,2,0,1,0,0xABCDEF0123456789,0xABCDEF0123456789,1,1,0,8,0xDEADBEEF89ADBEEF},
- {0xB019,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,IMM12_OFFSET,NA,4,1,0,1,0xABCDEF0123456789,0xABCDEF0123456789,5,1,0,8,0xDEADBEEFDEAD89EF},
- {0xB020,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,1,1,0,8,0xDEADBEEFDEAD89EF},
- {0xB021,INSTR_STRH,AL,AL,1,4066,0xDEADBEEFDEADBEEF,4070,IMM8_OFFSET,NA,2,1,0,1,0xABCDEF0123456789,0xABCDEF0123456789,4072,1,4066,8,0xDEAD6789DEADBEEF},
- {0xB022,INSTR_STRH,AL,AL,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEAD6789DEADBEEF},
-};
-
-
-void flushcache()
-{
- const long base = long(instrMem);
- const long curr = base + long(instrMemSize);
- __builtin___clear_cache((char*)base, (char*)curr);
-}
-
-void dataOpTest(dataOpTest_t test, ArmToMips64Assembler *a64asm, uint32_t Rd = R_v1,
- uint32_t Rn = R_t0, uint32_t Rm = R_t1, uint32_t Rs = R_t2)
-{
- int64_t regs[NUM_REGS] = {0};
- int32_t flags[NUM_FLAGS] = {0};
- int64_t savedRegs[NUM_REGS] = {0};
- uint32_t i;
- uint32_t op2;
-
- for(i = 0; i < NUM_REGS; ++i)
- {
- regs[i] = i;
- }
-
- regs[Rd] = test.RdValue;
- regs[Rn] = test.RnValue;
- regs[Rs] = test.RsValue;
- a64asm->reset();
- if (test.preCond.mode) {
- a64asm->set_condition(test.preCond.mode, test.preCond.Rcond1, test.preCond.Rcond2);
- regs[test.preCond.Rcond1] = test.preCond.Rcond1Value;
- regs[test.preCond.Rcond2] = test.preCond.Rcond2Value;
- }
- a64asm->prolog();
- if(test.immediate == true)
- {
- op2 = a64asm->imm(test.immValue);
- }
- else if(test.immediate == false && test.shiftAmount == 0)
- {
- op2 = Rm;
- regs[Rm] = (int64_t)((int32_t)(test.RmValue));
- }
- else
- {
- op2 = a64asm->reg_imm(Rm, test.shiftMode, test.shiftAmount);
- regs[Rm] = (int64_t)((int32_t)(test.RmValue));
- }
- switch(test.op)
- {
- case INSTR_ADD: a64asm->ADD(test.cond, test.setFlags, Rd,Rn,op2); break;
- case INSTR_SUB: a64asm->SUB(test.cond, test.setFlags, Rd,Rn,op2); break;
- case INSTR_RSB: a64asm->RSB(test.cond, test.setFlags, Rd,Rn,op2); break;
- case INSTR_AND: a64asm->AND(test.cond, test.setFlags, Rd,Rn,op2); break;
- case INSTR_ORR: a64asm->ORR(test.cond, test.setFlags, Rd,Rn,op2); break;
- case INSTR_BIC: a64asm->BIC(test.cond, test.setFlags, Rd,Rn,op2); break;
- case INSTR_MUL: a64asm->MUL(test.cond, test.setFlags, Rd,Rm,Rs); break;
- case INSTR_MLA: a64asm->MLA(test.cond, test.setFlags, Rd,Rm,Rs,Rn); break;
- case INSTR_CMP: a64asm->CMP(test.cond, Rn,op2); break;
- case INSTR_MOV: a64asm->MOV(test.cond, test.setFlags,Rd,op2); break;
- case INSTR_MVN: a64asm->MVN(test.cond, test.setFlags,Rd,op2); break;
- case INSTR_SMULBB:a64asm->SMULBB(test.cond, Rd,Rm,Rs); break;
- case INSTR_SMULBT:a64asm->SMULBT(test.cond, Rd,Rm,Rs); break;
- case INSTR_SMULTB:a64asm->SMULTB(test.cond, Rd,Rm,Rs); break;
- case INSTR_SMULTT:a64asm->SMULTT(test.cond, Rd,Rm,Rs); break;
- case INSTR_SMULWB:a64asm->SMULWB(test.cond, Rd,Rm,Rs); break;
- case INSTR_SMULWT:a64asm->SMULWT(test.cond, Rd,Rm,Rs); break;
- case INSTR_SMLABB:a64asm->SMLABB(test.cond, Rd,Rm,Rs,Rn); break;
- case INSTR_UXTB16:a64asm->UXTB16(test.cond, Rd,Rm,test.shiftAmount); break;
- case INSTR_ADDR_ADD: a64asm->ADDR_ADD(test.cond, test.setFlags, Rd,Rn,op2); break;
- case INSTR_ADDR_SUB: a64asm->ADDR_SUB(test.cond, test.setFlags, Rd,Rn,op2); break;
- default: printf("Error"); return;
- }
- a64asm->epilog(0);
- a64asm->fix_branches();
- flushcache();
-
- asm_function_t asm_function = (asm_function_t)(instrMem);
-
- for(i = 0; i < NUM_REGS; ++i)
- savedRegs[i] = regs[i];
-
- asm_mips_test_jacket(asm_function, regs, flags);
-
- /* Check if all regs except Rd is same */
- for(i = 0; i < NUM_REGS; ++i)
- {
- if((i == Rd) || i == 2) continue;
- if(regs[i] != savedRegs[i])
- {
- printf("Test %x failed Reg(%d) tampered Expected(0x%" PRIx64 "),"
- "Actual(0x%" PRIx64 ") t\n", test.id, i, savedRegs[i],
- regs[i]);
- exit(0);
- return;
- }
- }
-
- if(test.checkRd == 1 && regs[Rd] != test.postRdValue)
- {
- printf("Test %x failed, Expected(%" PRIx64 "), Actual(%" PRIx64 ")\n",
- test.id, test.postRdValue, regs[Rd]);
- exit(0);
- }
- else
- {
- printf("Test %x passed\n", test.id);
- }
-}
-
-
-void dataTransferTest(dataTransferTest_t test, ARMAssemblerInterface *a64asm,
- uint32_t Rd = R_v1, uint32_t Rn = R_t0,uint32_t Rm = R_t1)
-{
- int64_t regs[NUM_REGS] = {0};
- int64_t savedRegs[NUM_REGS] = {0};
- int32_t flags[NUM_FLAGS] = {0};
- uint32_t i;
- for(i = 0; i < NUM_REGS; ++i)
- {
- regs[i] = i;
- }
-
- uint32_t op2;
-
- regs[Rd] = test.RdValue;
- regs[Rn] = (uint64_t)(&dataMem[test.RnValue]);
- regs[Rm] = test.RmValue;
- flags[test.preFlag] = 1;
-
- if(test.setMem == true)
- {
- unsigned char *mem = (unsigned char *)&dataMem[test.memOffset];
- uint64_t value = test.memValue;
- for(int j = 0; j < 8; ++j)
- {
- mem[j] = value & 0x00FF;
- value >>= 8;
- }
- }
- a64asm->reset();
- a64asm->prolog();
- if(test.offsetType == REG_SCALE_OFFSET)
- {
- op2 = a64asm->reg_scale_pre(Rm);
- }
- else if(test.offsetType == REG_OFFSET)
- {
- op2 = a64asm->reg_pre(Rm);
- }
- else if(test.offsetType == IMM12_OFFSET && test.preIndex == true)
- {
- op2 = a64asm->immed12_pre(test.immValue, test.writeBack);
- }
- else if(test.offsetType == IMM12_OFFSET && test.postIndex == true)
- {
- op2 = a64asm->immed12_post(test.immValue);
- }
- else if(test.offsetType == IMM8_OFFSET && test.preIndex == true)
- {
- op2 = a64asm->immed8_pre(test.immValue, test.writeBack);
- }
- else if(test.offsetType == IMM8_OFFSET && test.postIndex == true)
- {
- op2 = a64asm->immed8_post(test.immValue);
- }
- else if(test.offsetType == NO_OFFSET)
- {
- op2 = a64asm->__immed12_pre(0);
- }
- else
- {
- printf("Error - Unknown offset\n"); return;
- }
-
- switch(test.op)
- {
- case INSTR_LDR: a64asm->LDR(test.cond, Rd,Rn,op2); break;
- case INSTR_LDRB: a64asm->LDRB(test.cond, Rd,Rn,op2); break;
- case INSTR_LDRH: a64asm->LDRH(test.cond, Rd,Rn,op2); break;
- case INSTR_ADDR_LDR: a64asm->ADDR_LDR(test.cond, Rd,Rn,op2); break;
- case INSTR_STR: a64asm->STR(test.cond, Rd,Rn,op2); break;
- case INSTR_STRB: a64asm->STRB(test.cond, Rd,Rn,op2); break;
- case INSTR_STRH: a64asm->STRH(test.cond, Rd,Rn,op2); break;
- case INSTR_ADDR_STR: a64asm->ADDR_STR(test.cond, Rd,Rn,op2); break;
- default: printf("Error"); return;
- }
- a64asm->epilog(0);
- flushcache();
-
- asm_function_t asm_function = (asm_function_t)(instrMem);
-
- for(i = 0; i < NUM_REGS; ++i)
- savedRegs[i] = regs[i];
-
- asm_mips_test_jacket(asm_function, regs, flags);
-
- /* Check if all regs except Rd/Rn are same */
- for(i = 0; i < NUM_REGS; ++i)
- {
- if(i == Rd || i == Rn || i == R_v0) continue;
-
- if(regs[i] != savedRegs[i])
- {
- printf("Test %x failed Reg(%d) tampered"
- " Expected(0x%" PRIx64 "), Actual(0x%" PRIx64 ") t\n",
- test.id, i, savedRegs[i], regs[i]);
- return;
- }
- }
-
- if((uint64_t)regs[Rd] != test.postRdValue)
- {
- printf("Test %x failed, "
- "Expected in Rd(0x%" PRIx64 "), Actual(0x%" PRIx64 ")\n",
- test.id, test.postRdValue, regs[Rd]);
- }
- else if((uint64_t)regs[Rn] != (uint64_t)(&dataMem[test.postRnValue]))
- {
- printf("Test %x failed, "
- "Expected in Rn(0x%" PRIx64 "), Actual(0x%" PRIx64 ")\n",
- test.id, test.postRnValue, regs[Rn] - (uint64_t)dataMem);
- }
- else if(test.checkMem == true)
- {
- unsigned char *addr = (unsigned char *)&dataMem[test.postMemOffset];
- uint64_t value;
- value = 0;
- for(uint32_t j = 0; j < test.postMemLength; ++j)
- value = (value << 8) | addr[test.postMemLength-j-1];
- if(value != test.postMemValue)
- {
- printf("Test %x failed, "
- "Expected in Mem(0x%" PRIx64 "), Actual(0x%" PRIx64 ")\n",
- test.id, test.postMemValue, value);
- }
- else
- {
- printf("Test %x passed\n", test.id);
- }
- }
- else
- {
- printf("Test %x passed\n", test.id);
- }
-}
-
-int main(void)
-{
- uint32_t i;
-
- /* Allocate memory to store instructions generated by ArmToArm64Assembler */
- {
- int fd = ashmem_create_region("code cache", instrMemSize);
- if(fd < 0) {
- printf("IF < 0\n");
- printf("Creating code cache, ashmem_create_region "
- "failed with error '%s'", strerror(errno));
- }
- instrMem = mmap(NULL, instrMemSize,
- PROT_READ | PROT_WRITE | PROT_EXEC,
- MAP_PRIVATE, fd, 0);
- }
-
- ArmToMips64Assembler a64asm(instrMem);
-
- if(TESTS_DATAOP_ENABLE)
- {
- printf("Running data processing tests\n");
- for(i = 0; i < sizeof(dataOpTests)/sizeof(dataOpTest_t); ++i) {
- dataOpTest(dataOpTests[i], &a64asm);
- }
- }
-
- if(TESTS_DATATRANSFER_ENABLE)
- {
- printf("Running data transfer tests\n");
- for(i = 0; i < sizeof(dataTransferTests)/sizeof(dataTransferTest_t); ++i)
- dataTransferTest(dataTransferTests[i], &a64asm);
- }
-
- return 0;
-}
diff --git a/libpixelflinger/tests/arch-mips64/col32cb16blend/Android.bp b/libpixelflinger/tests/arch-mips64/col32cb16blend/Android.bp
deleted file mode 100644
index bfc6ae9..0000000
--- a/libpixelflinger/tests/arch-mips64/col32cb16blend/Android.bp
+++ /dev/null
@@ -1,6 +0,0 @@
-cc_test {
- name: "test-pixelflinger-mips64-col32cb16blend",
- defaults: ["pixelflinger-tests-mips64"],
-
- srcs: ["col32cb16blend_test.c"],
-}
diff --git a/libpixelflinger/tests/arch-mips64/col32cb16blend/col32cb16blend_test.c b/libpixelflinger/tests/arch-mips64/col32cb16blend/col32cb16blend_test.c
deleted file mode 100644
index 066eab6..0000000
--- a/libpixelflinger/tests/arch-mips64/col32cb16blend/col32cb16blend_test.c
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <assert.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <inttypes.h>
-
-
-#define ARGB_8888_MAX 0xFFFFFFFF
-#define ARGB_8888_MIN 0x00000000
-#define RGB_565_MAX 0xFFFF
-#define RGB_565_MIN 0x0000
-
-struct test_t
-{
- char name[256];
- uint32_t src_color;
- uint16_t dst_color;
- size_t count;
-};
-
-struct test_t tests[] =
-{
- {"Count 1, Src=Max, Dst=Min", ARGB_8888_MAX, RGB_565_MIN, 1},
- {"Count 2, Src=Min, Dst=Max", ARGB_8888_MIN, RGB_565_MAX, 2},
- {"Count 3, Src=Max, Dst=Max", ARGB_8888_MAX, RGB_565_MAX, 3},
- {"Count 4, Src=Min, Dst=Min", ARGB_8888_MAX, RGB_565_MAX, 4},
- {"Count 1, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 1},
- {"Count 2, Src=Rand, Dst=Rand", 0xABCDEF12, 0x2345, 2},
- {"Count 3, Src=Rand, Dst=Rand", 0x11111111, 0xEDFE, 3},
- {"Count 4, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 4},
- {"Count 5, Src=Rand, Dst=Rand", 0xEFEFFEFE, 0xFACC, 5},
- {"Count 10, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 10}
-};
-
-void scanline_col32cb16blend_mips64(uint16_t *dst, uint32_t src, size_t count);
-void scanline_col32cb16blend_c(uint16_t * dst, uint32_t src, size_t count)
-{
- uint32_t srcAlpha = (src>>24);
- uint32_t f = 0x100 - (srcAlpha + (srcAlpha>>7));
-
- while (count--)
- {
- uint16_t d = *dst;
- int dstR = (d>>11)&0x1f;
- int dstG = (d>>5)&0x3f;
- int dstB = (d)&0x1f;
- int srcR = (src >> ( 3))&0x1F;
- int srcG = (src >> ( 8+2))&0x3F;
- int srcB = (src >> (16+3))&0x1F;
- srcR += (f*dstR)>>8;
- srcG += (f*dstG)>>8;
- srcB += (f*dstB)>>8;
- *dst++ = (uint16_t)((srcR<<11)|(srcG<<5)|srcB);
- }
-}
-
-void scanline_col32cb16blend_test()
-{
- uint16_t dst_c[16], dst_asm[16];
- uint32_t i, j;
-
- for(i = 0; i < sizeof(tests)/sizeof(struct test_t); ++i)
- {
- struct test_t test = tests[i];
-
- printf("Testing - %s:",test.name);
-
- memset(dst_c, 0, sizeof(dst_c));
- memset(dst_asm, 0, sizeof(dst_asm));
-
- for(j = 0; j < test.count; ++j)
- {
- dst_c[j] = test.dst_color;
- dst_asm[j] = test.dst_color;
- }
-
-
- scanline_col32cb16blend_c(dst_c, test.src_color, test.count);
- scanline_col32cb16blend_mips64(dst_asm, test.src_color, test.count);
-
- if(memcmp(dst_c, dst_asm, sizeof(dst_c)) == 0)
- printf("Passed\n");
- else
- printf("Failed\n");
-
- for(j = 0; j < test.count; ++j)
- {
- printf("dst_c[%d] = %x, dst_asm[%d] = %x \n", j, dst_c[j], j, dst_asm[j]);
- }
- }
-}
-
-int main()
-{
- scanline_col32cb16blend_test();
- return 0;
-}
diff --git a/libpixelflinger/tests/arch-mips64/disassembler/Android.bp b/libpixelflinger/tests/arch-mips64/disassembler/Android.bp
deleted file mode 100644
index 96bf9e9..0000000
--- a/libpixelflinger/tests/arch-mips64/disassembler/Android.bp
+++ /dev/null
@@ -1,6 +0,0 @@
-cc_test {
- name: "test-pixelflinger-mips64-disassembler-test",
- defaults: ["pixelflinger-tests-mips64"],
-
- srcs: ["mips64_disassembler_test.cpp"],
-}
diff --git a/libpixelflinger/tests/arch-mips64/disassembler/mips64_disassembler_test.cpp b/libpixelflinger/tests/arch-mips64/disassembler/mips64_disassembler_test.cpp
deleted file mode 100644
index 22efa9f..0000000
--- a/libpixelflinger/tests/arch-mips64/disassembler/mips64_disassembler_test.cpp
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-#include <stdio.h>
-#include <inttypes.h>
-#include <string.h>
-#include "../../../codeflinger/mips64_disassem.h"
-
-//typedef uint64_t db_addr_t;
-//db_addr_t mips_disassem(db_addr_t loc, char *di_buffer, int alt_format);
-
-struct test_table_entry_t
-{
- uint32_t code;
- const char *instr;
-};
-
-static test_table_entry_t test_table [] =
-{
- { 0x00011020, "add\tv0,zero,at" },
- { 0x00832820, "add\ta1,a0,v1" },
- { 0x00c74020, "add\ta4,a2,a3" },
- { 0x012a5820, "add\ta7,a5,a6" },
- { 0x258dffff, "addiu\tt1,t0,-1" },
- { 0x25cf0004, "addiu\tt3,t2,4" },
- { 0x02119021, "addu\ts2,s0,s1" },
- { 0x0274a821, "addu\ts5,s3,s4" },
- { 0x02d7c024, "and\tt8,s6,s7" },
- { 0x333aff00, "andi\tk0,t9,0xff00" },
- { 0x3f7cffff, "aui\tgp,k1,-1" },
- { 0x3c1dffff, "lui\tsp,0xffff" },
- { 0x00e04051, "clo\ta4,a3" },
- { 0x01205050, "clz\ta6,a5" },
- { 0x016c682c, "dadd\tt1,a7,t0" },
- { 0x65cf0008, "daddiu\tt3,t2,8" },
- { 0x0211902d, "daddu\ts2,s0,s1" },
- { 0x7e741403, "dext\ts4,s3,16,3" },
- { 0x7eb6f801, "dextm\ts6,s5,0,64" },
- { 0x7ef87c02, "dextu\tt8,s7,48,16" },
- { 0x7f3a8207, "dins\tk0,t9,8,9" },
- { 0x7f7c0005, "dinsm\tgp,k1,0,33" },
- { 0x7fbe0806, "dinsu\ts8,sp,32,2" },
- { 0x03e1102e, "dsub\tv0,ra,at" },
- { 0x0064282f, "dsubu\ta1,v1,a0" },
- { 0x7cc77a00, "ext\ta3,a2,8,16" },
- { 0x7d09fc04, "ins\ta5,a4,16,16" },
- { 0x00200009, "jr\tat" },
- { 0x00201009, "jalr\tv0,at" },
- { 0x0020f809, "jalr\tat" },
- { 0x8082fff0, "lb\tv0,-16(a0)" },
- { 0x916c0008, "lbu\tt0,8(a7)" },
- { 0xdfa3ffe8, "ld\tv1,-24(sp)" },
- { 0x84850080, "lh\ta1,128(a0)" },
- { 0x94c7ff80, "lhu\ta3,-128(a2)" },
- { 0x8d09000c, "lw\ta5,12(a4)" },
- { 0x9d4bfff4, "lwu\ta7,-12(a6)" },
- { 0x00620898, "mul\tat,v1,v0" },
- { 0x006208d8, "muh\tat,v1,v0" },
- { 0x00620899, "mulu\tat,v1,v0" },
- { 0x006208d9, "muhu\tat,v1,v0" },
- { 0x00000000, "nop" },
- { 0x02329827, "nor\ts3,s1,s2" },
- { 0x0295b025, "or\ts6,s4,s5" },
- { 0x36f0ff00, "ori\ts0,s7,0xff00" },
- { 0x7c03103b, "rdhwr\tv0,v1" },
- { 0x00242a02, "rotr\ta1,a0,8" },
- { 0x00c74046, "rotrv\ta4,a3,a2" },
- { 0xa12afff0, "sb\ta6,-16(a5)" },
- { 0xfd6c0100, "sd\tt0,256(a7)" },
- { 0x7c0d7420, "seb\tt2,t1" },
- { 0x7c0f8620, "seh\ts0,t3" },
- { 0x02329835, "seleqz\ts3,s1,s2" },
- { 0x0295b037, "selnez\ts6,s4,s5" },
- { 0xa6f84000, "sh\tt8,16384(s7)" },
- { 0x0019d100, "sll\tk0,t9,4" },
- { 0x037ce804, "sllv\tsp,gp,k1" },
- { 0x03df082a, "slt\tat,s8,ra" },
- { 0x28430007, "slti\tv1,v0,7" },
- { 0x2c850020, "sltiu\ta1,a0,32" },
- { 0x00c7402b, "sltu\ta4,a2,a3" },
- { 0x00095103, "sra\ta6,a5,4" },
- { 0x016c6807, "srav\tt1,t0,a7" },
- { 0x000e7a02, "srl\tt3,t2,8" },
- { 0x02119006, "srlv\ts2,s1,s0" },
- { 0x0274a822, "sub\ts5,s3,s4" },
- { 0x02d7c023, "subu\tt8,s6,s7" },
- { 0xaf3afffc, "sw\tk0,-4(t9)" },
- { 0x7c1be0a0, "wsbh\tgp,k1" },
- { 0x03bef826, "xor\tra,sp,s8" },
- { 0x3801ffff, "li\tat,0xffff" },
- { 0x3843ffff, "xori\tv1,v0,0xffff" },
-};
-
-struct test_branches_table_entry_t
-{
- uint32_t code;
- const char *instr;
- int16_t offset;
-};
-
-static test_branches_table_entry_t test_branches_table [] = {
- { 0x1000ffff, "b\t", static_cast<int16_t>(0xffff) },
- { 0x13df0008, "beq\ts8,ra,", 0x8 },
- { 0x042100ff, "bgez\tat,", 0xff },
- { 0x1c40ff00, "bgtz\tv0,", static_cast<int16_t>(0xff00) },
- { 0x18605555, "blez\tv1,", 0x5555 },
- { 0x0480aaaa, "bltz\ta0,", static_cast<int16_t>(0xaaaa) },
- { 0x14a68888, "bne\ta1,a2,", static_cast<int16_t>(0x8888) },
-};
-
-struct test_jump_table_entry_t
-{
- uint32_t code;
- const char *instr;
- int32_t offset;
-};
-
-static test_jump_table_entry_t test_jump_table [] = {
- { 0x0956ae66, "j\t", 0x156ae66 },
- { 0x0d56ae66, "jal\t", 0x156ae66 },
-};
-
-int main()
-{
- char instr[256];
- uint32_t failed = 0;
-
- for(uint32_t i = 0; i < sizeof(test_table)/sizeof(test_table_entry_t); ++i)
- {
- test_table_entry_t *test;
- test = &test_table[i];
- mips_disassem(&test->code, instr, 0);
- if(strcmp(instr, test->instr) != 0)
- {
- printf("Test Failed \n"
- "Code : 0x%0x\n"
- "Expected : %s\n"
- "Actual : %s\n", test->code, test->instr, instr);
- failed++;
- }
- }
- for(uint32_t i = 0; i < sizeof(test_branches_table)/sizeof(test_branches_table_entry_t); ++i)
- {
- test_branches_table_entry_t *test;
- test = &test_branches_table[i];
- mips_disassem(&test->code, instr, 0);
- //printf("DBG code address: %lx\n", (uint64_t)(&test->code));
- uint64_t loc = (uint64_t)test + 4 + (test->offset << 2);
- //printf("DBG loc: %lx\n", loc);
- char temp[256], address[16];
- strcpy(temp, test->instr);
- sprintf(address, "0x%lx", loc);
- strcat(temp, address);
- if(strcmp(instr, temp) != 0)
- {
- printf("Test Failed \n"
- "Code : 0x%0x\n"
- "Expected : %s\n"
- "Actual : %s\n", test->code, temp, instr);
- failed++;
- }
- }
- for(uint32_t i = 0; i < sizeof(test_jump_table)/sizeof(test_jump_table_entry_t); ++i)
- {
- test_jump_table_entry_t *test;
- test = &test_jump_table[i];
- mips_disassem(&test->code, instr, 0);
- //printf("DBG code address: %lx\n", (uint64_t)(&test->code));
- uint64_t loc = ((uint64_t)test & 0xfffffffff0000000) | (test->offset << 2);
- //printf("DBG loc: %lx\n", loc);
- char temp[256], address[16];
- strcpy(temp, test->instr);
- sprintf(address, "0x%08lx", loc);
- strcat(temp, address);
- if(strcmp(instr, temp) != 0)
- {
- printf("Test Failed \n"
- "Code : 0x%0x\n"
- "Expected : '%s'\n"
- "Actual : '%s'\n", test->code, temp, instr);
- failed++;
- }
- }
- if(failed == 0)
- {
- printf("All tests PASSED\n");
- return 0;
- }
- else
- {
- printf("%d tests FAILED\n", failed);
- return -1;
- }
-}
diff --git a/libprocessgroup/Android.bp b/libprocessgroup/Android.bp
index 618a5c5..bda11e9 100644
--- a/libprocessgroup/Android.bp
+++ b/libprocessgroup/Android.bp
@@ -13,6 +13,11 @@
enabled: true,
},
},
+ apex_available: [
+ "//apex_available:platform",
+ "//apex_available:anyapex",
+ ],
+ min_sdk_version: "29",
}
cc_library {
@@ -52,4 +57,9 @@
"-Werror",
"-Wexit-time-destructors",
],
+ apex_available: [
+ "//apex_available:platform",
+ "//apex_available:anyapex",
+ ],
+ min_sdk_version: "29",
}
diff --git a/libprocessgroup/profiles/Android.bp b/libprocessgroup/profiles/Android.bp
index e05a690..ccc6f62 100644
--- a/libprocessgroup/profiles/Android.bp
+++ b/libprocessgroup/profiles/Android.bp
@@ -89,20 +89,23 @@
"test_vendor.cpp",
],
static_libs: [
+ "libbase",
"libgmock",
+ "liblog",
+ "libjsoncpp",
"libjsonpbverify",
"libjsonpbparse",
"libprocessgroup_proto",
],
shared_libs: [
- "libbase",
- "liblog",
- "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/libprocessgroup/profiles/Android.mk b/libprocessgroup/profiles/Android.mk
deleted file mode 100644
index eab96d4..0000000
--- a/libprocessgroup/profiles/Android.mk
+++ /dev/null
@@ -1,21 +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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := VtsProcessgroupValidateTest
--include test/vts/tools/build/Android.host_config.mk
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index 3f08535..a515e58 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -144,6 +144,19 @@
}
]
},
+ {
+ "Name": "CameraServicePerformance",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "schedtune",
+ "Path": "camera-daemon"
+ }
+ }
+ ]
+ },
{
"Name": "CpuPolicySpread",
diff --git a/libprocinfo/Android.bp b/libprocinfo/Android.bp
index 0c9a2b8..15b0d89 100644
--- a/libprocinfo/Android.bp
+++ b/libprocinfo/Android.bp
@@ -51,6 +51,12 @@
enabled: false,
},
},
+
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.art.debug",
+ "com.android.art.release",
+ ],
}
// Tests
diff --git a/libsparse/output_file.cpp b/libsparse/output_file.cpp
index e35cb0d..b883c13 100644
--- a/libsparse/output_file.cpp
+++ b/libsparse/output_file.cpp
@@ -35,8 +35,9 @@
#include "sparse_crc32.h"
#include "sparse_format.h"
+#include <android-base/mapped_file.h>
+
#ifndef _WIN32
-#include <sys/mman.h>
#define O_BINARY 0
#else
#define ftruncate64 ftruncate
@@ -45,7 +46,6 @@
#if defined(__APPLE__) && defined(__MACH__)
#define lseek64 lseek
#define ftruncate64 ftruncate
-#define mmap64 mmap
#define off64_t off_t
#endif
@@ -649,52 +649,10 @@
}
int write_fd_chunk(struct output_file* out, unsigned int len, int fd, int64_t offset) {
- int ret;
- int64_t aligned_offset;
- int aligned_diff;
- uint64_t buffer_size;
- char* ptr;
+ auto m = android::base::MappedFile::FromFd(fd, offset, len, PROT_READ);
+ if (!m) return -errno;
- aligned_offset = offset & ~(4096 - 1);
- aligned_diff = offset - aligned_offset;
- buffer_size = (uint64_t)len + (uint64_t)aligned_diff;
-
-#ifndef _WIN32
- if (buffer_size > SIZE_MAX) return -E2BIG;
- char* data =
- reinterpret_cast<char*>(mmap64(nullptr, buffer_size, PROT_READ, MAP_SHARED, fd, aligned_offset));
- if (data == MAP_FAILED) {
- return -errno;
- }
- ptr = data + aligned_diff;
-#else
- off64_t pos;
- char* data = reinterpret_cast<char*>(malloc(len));
- if (!data) {
- return -errno;
- }
- pos = lseek64(fd, offset, SEEK_SET);
- if (pos < 0) {
- free(data);
- return -errno;
- }
- ret = read_all(fd, data, len);
- if (ret < 0) {
- free(data);
- return ret;
- }
- ptr = data;
-#endif
-
- ret = out->sparse_ops->write_data_chunk(out, len, ptr);
-
-#ifndef _WIN32
- munmap(data, buffer_size);
-#else
- free(data);
-#endif
-
- return ret;
+ return out->sparse_ops->write_data_chunk(out, m->size(), m->data());
}
/* Write a contiguous region of data blocks from a file */
diff --git a/libsparse/sparse.cpp b/libsparse/sparse.cpp
index 24c6379..8622b4c 100644
--- a/libsparse/sparse.cpp
+++ b/libsparse/sparse.cpp
@@ -136,11 +136,23 @@
return 0;
}
+/*
+ * This is a workaround for 32-bit Windows: Limit the block size to 64 MB before
+ * fastboot executable binary for windows 64-bit is released (b/156057250).
+ */
+#define MAX_BACKED_BLOCK_SIZE ((unsigned int) (64UL << 20))
+
int sparse_file_write(struct sparse_file* s, int fd, bool gz, bool sparse, bool crc) {
+ struct backed_block* bb;
int ret;
int chunks;
struct output_file* out;
+ for (bb = backed_block_iter_new(s->backed_block_list); bb; bb = backed_block_iter_next(bb)) {
+ ret = backed_block_split(s->backed_block_list, bb, MAX_BACKED_BLOCK_SIZE);
+ if (ret) return ret;
+ }
+
chunks = sparse_count_chunks(s);
out = output_file_open_fd(fd, s->block_size, s->len, gz, sparse, chunks, crc);
diff --git a/libstats/push_compat/Android.bp b/libstats/push_compat/Android.bp
index 465c05a..cbc65ff 100644
--- a/libstats/push_compat/Android.bp
+++ b/libstats/push_compat/Android.bp
@@ -35,6 +35,8 @@
header_libs: ["libstatssocket_headers"],
static_libs: [
"libbase",
+ ],
+ shared_libs: [
"liblog",
"libutils",
],
@@ -45,6 +47,8 @@
defaults: ["libstatspush_compat_defaults"],
export_include_dirs: ["include"],
static_libs: ["libgtest_prod"],
+ apex_available: ["com.android.resolv"],
+ min_sdk_version: "29",
}
cc_test {
diff --git a/libstats/socket/Android.bp b/libstats/socket/Android.bp
index 9fd9fbc..6882ab2 100644
--- a/libstats/socket/Android.bp
+++ b/libstats/socket/Android.bp
@@ -54,6 +54,8 @@
name: "libstatssocket_headers",
export_include_dirs: ["include"],
host_supported: true,
+ apex_available: ["com.android.resolv"],
+ min_sdk_version: "29",
}
cc_benchmark {
diff --git a/libsync/Android.bp b/libsync/Android.bp
index c996e1b..bad6230 100644
--- a/libsync/Android.bp
+++ b/libsync/Android.bp
@@ -25,6 +25,12 @@
recovery_available: true,
native_bridge_supported: true,
defaults: ["libsync_defaults"],
+ stubs: {
+ symbol_file: "libsync.map.txt",
+ versions: [
+ "26",
+ ],
+ },
}
llndk_library {
diff --git a/libsync/libsync.map.txt b/libsync/libsync.map.txt
index 91c3528..aac6b57 100644
--- a/libsync/libsync.map.txt
+++ b/libsync/libsync.map.txt
@@ -19,7 +19,7 @@
sync_merge; # introduced=26
sync_file_info; # introduced=26
sync_file_info_free; # introduced=26
- sync_wait; # llndk
+ sync_wait; # llndk apex
sync_fence_info; # llndk
sync_pt_info; # llndk
sync_fence_info_free; # llndk
diff --git a/libsystem/Android.bp b/libsystem/Android.bp
index b265b61..db61669 100644
--- a/libsystem/Android.bp
+++ b/libsystem/Android.bp
@@ -4,6 +4,11 @@
recovery_available: true,
host_supported: true,
native_bridge_supported: true,
+ apex_available: [
+ "//apex_available:platform",
+ "//apex_available:anyapex",
+ ],
+ min_sdk_version: "apex_inherit",
export_include_dirs: ["include"],
target: {
diff --git a/libsysutils/Android.bp b/libsysutils/Android.bp
index 627f0d4..3b98bab 100644
--- a/libsysutils/Android.bp
+++ b/libsysutils/Android.bp
@@ -43,6 +43,7 @@
"//apex_available:anyapex",
"//apex_available:platform",
],
+ min_sdk_version: "apex_inherit",
}
cc_test {
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index 2351afa..9c1621b 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -180,6 +180,7 @@
struct ifa_cacheinfo *cacheinfo = nullptr;
char addrstr[INET6_ADDRSTRLEN] = "";
char ifname[IFNAMSIZ] = "";
+ uint32_t flags;
if (!checkRtNetlinkLength(nh, sizeof(*ifaddr)))
return false;
@@ -194,6 +195,9 @@
// For log messages.
const char *msgtype = rtMessageName(type);
+ // First 8 bits of flags. In practice will always be overridden when parsing IFA_FLAGS below.
+ flags = ifaddr->ifa_flags;
+
struct rtattr *rta;
int len = IFA_PAYLOAD(nh);
for (rta = IFA_RTA(ifaddr); RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
@@ -242,6 +246,9 @@
}
cacheinfo = (struct ifa_cacheinfo *) RTA_DATA(rta);
+
+ } else if (rta->rta_type == IFA_FLAGS) {
+ flags = *(uint32_t*)RTA_DATA(rta);
}
}
@@ -256,7 +263,7 @@
mSubsystem = strdup("net");
asprintf(&mParams[0], "ADDRESS=%s/%d", addrstr, ifaddr->ifa_prefixlen);
asprintf(&mParams[1], "INTERFACE=%s", ifname);
- asprintf(&mParams[2], "FLAGS=%u", ifaddr->ifa_flags);
+ asprintf(&mParams[2], "FLAGS=%u", flags);
asprintf(&mParams[3], "SCOPE=%u", ifaddr->ifa_scope);
asprintf(&mParams[4], "IFINDEX=%u", ifaddr->ifa_index);
@@ -529,6 +536,10 @@
free(buf);
} else if (opthdr->nd_opt_type == ND_OPT_DNSSL) {
// TODO: support DNSSL.
+ } else if (opthdr->nd_opt_type == ND_OPT_CAPTIVE_PORTAL) {
+ // TODO: support CAPTIVE PORTAL.
+ } else if (opthdr->nd_opt_type == ND_OPT_PREF64) {
+ // TODO: support PREF64.
} else {
SLOGD("Unknown ND option type %d\n", opthdr->nd_opt_type);
return false;
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 3695f72..3c44534 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -35,20 +35,13 @@
},
}
-cc_library {
- name: "libunwindstack",
- vendor_available: true,
- recovery_available: true,
- vndk: {
- enabled: true,
- support_system_process: true,
- },
+cc_defaults {
+ name: "libunwindstack_defaults",
defaults: ["libunwindstack_flags"],
export_include_dirs: ["include"],
srcs: [
"ArmExidx.cpp",
- "DexFile.cpp",
"DexFiles.cpp",
"DwarfCfa.cpp",
"DwarfEhFrameWithHdr.cpp",
@@ -64,6 +57,7 @@
"MapInfo.cpp",
"Maps.cpp",
"Memory.cpp",
+ "MemoryMte.cpp",
"LocalUnwinder.cpp",
"Regs.cpp",
"RegsArm.cpp",
@@ -77,35 +71,32 @@
],
cflags: [
- "-DDEXFILE_SUPPORT",
"-Wexit-time-destructors",
],
target: {
- // Always disable optimizations for host to make it easier to debug.
host: {
+ // Always disable optimizations for host to make it easier to debug.
cflags: [
"-O0",
"-g",
],
},
- vendor: {
- cflags: ["-UDEXFILE_SUPPORT"],
- exclude_srcs: [
- "DexFile.cpp",
- ],
- exclude_shared_libs: [
- "libdexfile_support",
- ],
+ android: {
+ header_libs: ["bionic_libc_platform_headers"],
+ product_variables: {
+ experimental_mte: {
+ cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
+ },
+ },
},
- recovery: {
- cflags: ["-UDEXFILE_SUPPORT"],
- exclude_srcs: [
- "DexFile.cpp",
- ],
- exclude_shared_libs: [
- "libdexfile_support",
- ],
+ linux_bionic: {
+ header_libs: ["bionic_libc_platform_headers"],
+ product_variables: {
+ experimental_mte: {
+ cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
+ },
+ },
},
},
@@ -116,12 +107,6 @@
x86_64: {
srcs: ["AsmGetRegsX86_64.S"],
},
- mips: {
- srcs: ["AsmGetRegsMips.S"],
- },
- mips64: {
- srcs: ["AsmGetRegsMips64.S"],
- },
},
static_libs: [
@@ -130,12 +115,62 @@
shared_libs: [
"libbase",
- "libdexfile_support",
"liblog",
"liblzma",
],
}
+cc_library {
+ name: "libunwindstack",
+ vendor_available: true,
+ recovery_available: true,
+ vndk: {
+ enabled: true,
+ support_system_process: true,
+ },
+ defaults: ["libunwindstack_defaults"],
+
+ srcs: ["DexFile.cpp"],
+ cflags: ["-DDEXFILE_SUPPORT"],
+ shared_libs: ["libdexfile_support"],
+
+ target: {
+ vendor: {
+ cflags: ["-UDEXFILE_SUPPORT"],
+ exclude_srcs: ["DexFile.cpp"],
+ exclude_shared_libs: ["libdexfile_support"],
+ },
+ recovery: {
+ cflags: ["-UDEXFILE_SUPPORT"],
+ exclude_srcs: ["DexFile.cpp"],
+ exclude_shared_libs: ["libdexfile_support"],
+ },
+ },
+
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.art.debug",
+ "com.android.art.release",
+ ],
+}
+
+// Static library without DEX support to avoid dependencies on the ART APEX.
+cc_library_static {
+ name: "libunwindstack_no_dex",
+ recovery_available: true,
+ defaults: ["libunwindstack_defaults"],
+
+ visibility: [
+ "//system/core/debuggerd",
+ "//system/core/init",
+ "//system/core/libbacktrace",
+ ],
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.runtime",
+ ],
+}
+
//-------------------------------------------------------------------------
// Unit Tests
//-------------------------------------------------------------------------
@@ -201,6 +236,7 @@
"tests/MemoryRangesTest.cpp",
"tests/MemoryRemoteTest.cpp",
"tests/MemoryTest.cpp",
+ "tests/MemoryMteTest.cpp",
"tests/RegsInfoTest.cpp",
"tests/RegsIterateTest.cpp",
"tests/RegsStepIfSignalHandlerTest.cpp",
@@ -256,6 +292,25 @@
"tests/files/offline/straddle_arm/*",
"tests/files/offline/straddle_arm64/*",
],
+
+ target: {
+ android: {
+ header_libs: ["bionic_libc_platform_headers"],
+ product_variables: {
+ experimental_mte: {
+ cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
+ },
+ },
+ },
+ linux_bionic: {
+ header_libs: ["bionic_libc_platform_headers"],
+ product_variables: {
+ experimental_mte: {
+ cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
+ },
+ },
+ },
+ },
}
cc_test {
@@ -361,12 +416,29 @@
srcs: [
"benchmarks/unwind_benchmarks.cpp",
+ "benchmarks/ElfBenchmark.cpp",
+ "benchmarks/MapsBenchmark.cpp",
+ "benchmarks/SymbolBenchmark.cpp",
+ "benchmarks/Utils.cpp",
+ ],
+
+ data: [
+ "benchmarks/files/*",
],
shared_libs: [
"libbase",
"libunwindstack",
],
+
+ target: {
+ android: {
+ static_libs: [
+ "libmeminfo",
+ "libprocinfo",
+ ],
+ },
+ },
}
// Generates the elf data for use in the tests for .gnu_debugdata frames.
diff --git a/libunwindstack/AsmGetRegsMips.S b/libunwindstack/AsmGetRegsMips.S
deleted file mode 100644
index 183d0a9..0000000
--- a/libunwindstack/AsmGetRegsMips.S
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
- .text
- .type AsmGetRegs, %function
- .globl AsmGetRegs
- .ent AsmGetRegs
- .balign 16
-AsmGetRegs:
- .cfi_startproc
- .cfi_def_cfa $sp, 0
- .set push
- .set noreorder
- .cpload $t9
- sw $zero, 0($a0)
- .set noat
- sw $at, 4($a0)
- .set at
- sw $v0, 8($a0)
- sw $v1, 12($a0)
- sw $a0, 16($a0)
- sw $a1, 20($a0)
- sw $a2, 24($a0)
- sw $a3, 28($a0)
- sw $t0, 32($a0)
- sw $t1, 36($a0)
- sw $t2, 40($a0)
- sw $t3, 44($a0)
- sw $t4, 48($a0)
- sw $t5, 52($a0)
- sw $t6, 56($a0)
- sw $t7, 60($a0)
- sw $s0, 64($a0)
- sw $s1, 68($a0)
- sw $s2, 72($a0)
- sw $s3, 76($a0)
- sw $s4, 80($a0)
- sw $s5, 84($a0)
- sw $s6, 88($a0)
- sw $s7, 92($a0)
- sw $t8, 96($a0)
- sw $t9, 100($a0)
- sw $k0, 104($a0)
- sw $k1, 108($a0)
- sw $gp, 112($a0)
- sw $sp, 116($a0)
- sw $s8, 120($a0)
- sw $ra, 124($a0)
- jalr $zero, $ra
- sw $ra, 128($a0) // set PC to the calling function
-
- .set pop
- .cfi_endproc
- .size AsmGetRegs, .-AsmGetRegs
- .end AsmGetRegs
diff --git a/libunwindstack/AsmGetRegsMips64.S b/libunwindstack/AsmGetRegsMips64.S
deleted file mode 100644
index 7a244f6..0000000
--- a/libunwindstack/AsmGetRegsMips64.S
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
- .text
- .type AsmGetRegs, %function
- .globl AsmGetRegs
- .ent AsmGetRegs
- .balign 16
-AsmGetRegs:
- .cfi_startproc
- .cfi_def_cfa $sp, 0
- .set push
- .set noreorder
- .cpload $t9
- sd $zero, 0($a0)
- .set noat
- sd $at, 8($a0)
- .set at
- sd $v0, 16($a0)
- sd $v1, 24($a0)
- sd $a0, 32($a0)
- sd $a1, 40($a0)
- sd $a2, 48($a0)
- sd $a3, 56($a0)
- sd $a4, 64($a0)
- sd $a5, 72($a0)
- sd $a6, 80($a0)
- sd $a7, 88($a0)
- sd $t0, 96($a0)
- sd $t1, 104($a0)
- sd $t2, 112($a0)
- sd $t3, 120($a0)
- sd $s0, 128($a0)
- sd $s1, 136($a0)
- sd $s2, 144($a0)
- sd $s3, 152($a0)
- sd $s4, 160($a0)
- sd $s5, 168($a0)
- sd $s6, 176($a0)
- sd $s7, 184($a0)
- sd $t8, 192($a0)
- sd $t9, 200($a0)
- sd $k0, 208($a0)
- sd $k1, 216($a0)
- sd $gp, 224($a0)
- sd $sp, 232($a0)
- sd $s8, 240($a0)
- sd $ra, 248($a0)
- jalr $zero, $ra
- sd $ra, 256($a0) // set PC to the calling function
-
- .set pop
- .cfi_endproc
- .size AsmGetRegs, .-AsmGetRegs
- .end AsmGetRegs
diff --git a/libunwindstack/DexFile.cpp b/libunwindstack/DexFile.cpp
index dff7a8b..8fc3d23 100644
--- a/libunwindstack/DexFile.cpp
+++ b/libunwindstack/DexFile.cpp
@@ -50,6 +50,22 @@
std::unique_ptr<DexFile> DexFile::Create(uint64_t dex_file_offset_in_memory, Memory* memory,
MapInfo* info) {
+ if (UNLIKELY(!HasDexSupport())) {
+ return nullptr;
+ }
+
+ size_t max_size = info->end - dex_file_offset_in_memory;
+ if (memory->IsLocal()) {
+ size_t size = max_size;
+
+ std::string err_msg;
+ std::unique_ptr<art_api::dex::DexFile> art_dex_file = DexFile::OpenFromMemory(
+ reinterpret_cast<void const*>(dex_file_offset_in_memory), &size, info->name, &err_msg);
+ if (art_dex_file != nullptr && size <= max_size) {
+ return std::unique_ptr<DexFile>(new DexFile(art_dex_file));
+ }
+ }
+
if (!info->name.empty()) {
std::unique_ptr<DexFile> dex_file =
DexFileFromFile::Create(dex_file_offset_in_memory - info->start + info->offset, info->name);
@@ -57,7 +73,7 @@
return dex_file;
}
}
- return DexFileFromMemory::Create(dex_file_offset_in_memory, memory, info->name);
+ return DexFileFromMemory::Create(dex_file_offset_in_memory, memory, info->name, max_size);
}
bool DexFile::GetMethodInformation(uint64_t dex_offset, std::string* method_name,
@@ -89,12 +105,13 @@
return nullptr;
}
- return std::unique_ptr<DexFileFromFile>(new DexFileFromFile(std::move(*art_dex_file.release())));
+ return std::unique_ptr<DexFileFromFile>(new DexFileFromFile(art_dex_file));
}
std::unique_ptr<DexFileFromMemory> DexFileFromMemory::Create(uint64_t dex_file_offset_in_memory,
Memory* memory,
- const std::string& name) {
+ const std::string& name,
+ size_t max_size) {
if (UNLIKELY(!HasDexSupport())) {
return nullptr;
}
@@ -105,10 +122,13 @@
std::string error_msg;
std::unique_ptr<art_api::dex::DexFile> art_dex_file =
OpenFromMemory(backing_memory.data(), &size, name, &error_msg);
+ if (size > max_size) {
+ return nullptr;
+ }
if (art_dex_file != nullptr) {
return std::unique_ptr<DexFileFromMemory>(
- new DexFileFromMemory(std::move(*art_dex_file.release()), std::move(backing_memory)));
+ new DexFileFromMemory(art_dex_file, std::move(backing_memory)));
}
if (!error_msg.empty()) {
diff --git a/libunwindstack/DexFile.h b/libunwindstack/DexFile.h
index ca658e6..fe185da 100644
--- a/libunwindstack/DexFile.h
+++ b/libunwindstack/DexFile.h
@@ -39,7 +39,8 @@
MapInfo* info);
protected:
- DexFile(art_api::dex::DexFile&& art_dex_file) : art_api::dex::DexFile(std::move(art_dex_file)) {}
+ DexFile(std::unique_ptr<art_api::dex::DexFile>& art_dex_file)
+ : art_api::dex::DexFile(art_dex_file) {}
};
class DexFileFromFile : public DexFile {
@@ -48,17 +49,19 @@
const std::string& file);
private:
- DexFileFromFile(art_api::dex::DexFile&& art_dex_file) : DexFile(std::move(art_dex_file)) {}
+ DexFileFromFile(std::unique_ptr<art_api::dex::DexFile>& art_dex_file) : DexFile(art_dex_file) {}
};
class DexFileFromMemory : public DexFile {
public:
static std::unique_ptr<DexFileFromMemory> Create(uint64_t dex_file_offset_in_memory,
- Memory* memory, const std::string& name);
+ Memory* memory, const std::string& name,
+ size_t max_size);
private:
- DexFileFromMemory(art_api::dex::DexFile&& art_dex_file, std::vector<uint8_t>&& memory)
- : DexFile(std::move(art_dex_file)), memory_(std::move(memory)) {}
+ DexFileFromMemory(std::unique_ptr<art_api::dex::DexFile>& art_dex_file,
+ std::vector<uint8_t>&& memory)
+ : DexFile(art_dex_file), memory_(std::move(memory)) {}
std::vector<uint8_t> memory_;
};
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index f01b092..286febc 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -124,6 +124,12 @@
return false;
}
+ if (arch() == ARCH_ARM64) {
+ // Tagged pointer after Android R would lead top byte to have random values
+ // https://source.android.com/devices/tech/debug/tagged-pointers
+ vaddr &= (1ULL << 56) - 1;
+ }
+
// Check the .data section.
uint64_t vaddr_start = interface_->data_vaddr_start();
if (vaddr >= vaddr_start && vaddr < interface_->data_vaddr_end()) {
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index 341275d..17470fd 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -371,7 +371,7 @@
// Look for the .debug_frame and .gnu_debugdata.
if (shdr.sh_name < sec_size) {
std::string name;
- if (memory_->ReadString(sec_offset + shdr.sh_name, &name)) {
+ if (memory_->ReadString(sec_offset + shdr.sh_name, &name, sec_size - shdr.sh_name)) {
if (name == ".debug_frame") {
debug_frame_offset_ = shdr.sh_offset;
debug_frame_size_ = shdr.sh_size;
@@ -405,7 +405,7 @@
} else if (shdr.sh_type == SHT_NOTE) {
if (shdr.sh_name < sec_size) {
std::string name;
- if (memory_->ReadString(sec_offset + shdr.sh_name, &name) &&
+ if (memory_->ReadString(sec_offset + shdr.sh_name, &name, sec_size - shdr.sh_name) &&
name == ".note.gnu.build-id") {
gnu_build_id_offset_ = shdr.sh_offset;
gnu_build_id_size_ = shdr.sh_size;
@@ -456,10 +456,11 @@
for (const auto& entry : strtabs_) {
if (entry.first == strtab_addr) {
soname_offset = entry.second + soname_offset;
- if (soname_offset >= entry.second + strtab_size) {
+ uint64_t soname_max = entry.second + strtab_size;
+ if (soname_offset >= soname_max) {
return "";
}
- if (!memory_->ReadString(soname_offset, &soname_)) {
+ if (!memory_->ReadString(soname_offset, &soname_, soname_max - soname_offset)) {
return "";
}
soname_type_ = SONAME_VALID;
@@ -608,7 +609,8 @@
}
std::string name;
if (shdr.sh_type == SHT_NOTE && shdr.sh_name < sec_size &&
- memory->ReadString(sec_offset + shdr.sh_name, &name) && name == ".note.gnu.build-id") {
+ memory->ReadString(sec_offset + shdr.sh_name, &name, sec_size - shdr.sh_name) &&
+ name == ".note.gnu.build-id") {
*build_id_offset = shdr.sh_offset;
*build_id_size = shdr.sh_size;
return true;
@@ -662,7 +664,7 @@
if (note_size - offset < hdr.n_descsz || hdr.n_descsz == 0) {
return "";
}
- std::string build_id(hdr.n_descsz - 1, '\0');
+ std::string build_id(hdr.n_descsz, '\0');
if (memory->ReadFully(note_offset + offset, &build_id[0], hdr.n_descsz)) {
return build_id;
}
diff --git a/libunwindstack/LocalUnwinder.cpp b/libunwindstack/LocalUnwinder.cpp
index 5d81200..05650fb 100644
--- a/libunwindstack/LocalUnwinder.cpp
+++ b/libunwindstack/LocalUnwinder.cpp
@@ -106,7 +106,7 @@
uint64_t step_pc = rel_pc;
uint64_t pc_adjustment;
if (adjust_pc) {
- pc_adjustment = regs->GetPcAdjustment(rel_pc, elf);
+ pc_adjustment = GetPcAdjustment(rel_pc, elf, arch);
} else {
pc_adjustment = 0;
}
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index 8f49ad9..670d904 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -159,6 +159,8 @@
search_map_idx = old_map_idx + 1;
if (new_map_idx + 1 < maps_.size()) {
maps_[new_map_idx + 1]->prev_map = info.get();
+ maps_[new_map_idx + 1]->prev_real_map =
+ info->IsBlank() ? info->prev_real_map : info.get();
}
maps_[new_map_idx] = nullptr;
total_entries--;
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp
index 8de3d98..b4623fa 100644
--- a/libunwindstack/Memory.cpp
+++ b/libunwindstack/Memory.cpp
@@ -158,20 +158,30 @@
return rc == size;
}
-bool Memory::ReadString(uint64_t addr, std::string* string, uint64_t max_read) {
- string->clear();
- uint64_t bytes_read = 0;
- while (bytes_read < max_read) {
- uint8_t value;
- if (!ReadFully(addr, &value, sizeof(value))) {
- return false;
+bool Memory::ReadString(uint64_t addr, std::string* dst, size_t max_read) {
+ char buffer[256]; // Large enough for 99% of symbol names.
+ size_t size = 0; // Number of bytes which were read into the buffer.
+ for (size_t offset = 0; offset < max_read; offset += size) {
+ // Look for null-terminator first, so we can allocate string of exact size.
+ // If we know the end of valid memory range, do the reads in larger blocks.
+ size_t read = std::min(sizeof(buffer), max_read - offset);
+ size = Read(addr + offset, buffer, read);
+ if (size == 0) {
+ return false; // We have not found end of string yet and we can not read more data.
}
- if (value == '\0') {
- return true;
+ size_t length = strnlen(buffer, size); // Index of the null-terminator.
+ if (length < size) {
+ // We found the null-terminator. Allocate the string and set its content.
+ if (offset == 0) {
+ // We did just single read, so the buffer already contains the whole string.
+ dst->assign(buffer, length);
+ return true;
+ } else {
+ // The buffer contains only the last block. Read the whole string again.
+ dst->assign(offset + length, '\0');
+ return ReadFully(addr, dst->data(), dst->size());
+ }
}
- string->push_back(value);
- addr++;
- bytes_read++;
}
return false;
}
@@ -324,6 +334,16 @@
return ProcessVmRead(getpid(), addr, dst, size);
}
+#if !defined(ANDROID_EXPERIMENTAL_MTE)
+long MemoryRemote::ReadTag(uint64_t) {
+ return -1;
+}
+
+long MemoryLocal::ReadTag(uint64_t) {
+ return -1;
+}
+#endif
+
MemoryRange::MemoryRange(const std::shared_ptr<Memory>& memory, uint64_t begin, uint64_t length,
uint64_t offset)
: memory_(memory), begin_(begin), length_(length), offset_(offset) {}
diff --git a/libunwindstack/MemoryCache.h b/libunwindstack/MemoryCache.h
index 769d907..d97640d 100644
--- a/libunwindstack/MemoryCache.h
+++ b/libunwindstack/MemoryCache.h
@@ -33,6 +33,7 @@
virtual ~MemoryCache() = default;
size_t Read(uint64_t addr, void* dst, size_t size) override;
+ long ReadTag(uint64_t addr) override { return impl_->ReadTag(addr); }
void Clear() override { cache_.clear(); }
diff --git a/libunwindstack/MemoryLocal.h b/libunwindstack/MemoryLocal.h
index 29aaf12..741f107 100644
--- a/libunwindstack/MemoryLocal.h
+++ b/libunwindstack/MemoryLocal.h
@@ -28,7 +28,10 @@
MemoryLocal() = default;
virtual ~MemoryLocal() = default;
+ bool IsLocal() const override { return true; }
+
size_t Read(uint64_t addr, void* dst, size_t size) override;
+ long ReadTag(uint64_t addr) override;
};
} // namespace unwindstack
diff --git a/libunwindstack/MemoryMte.cpp b/libunwindstack/MemoryMte.cpp
new file mode 100644
index 0000000..46a546e
--- /dev/null
+++ b/libunwindstack/MemoryMte.cpp
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+#if defined(ANDROID_EXPERIMENTAL_MTE)
+
+#include <sys/ptrace.h>
+#include <sys/uio.h>
+
+#include <bionic/mte.h>
+#include <bionic/mte_kernel.h>
+
+#include "MemoryLocal.h"
+#include "MemoryRemote.h"
+
+namespace unwindstack {
+
+long MemoryRemote::ReadTag(uint64_t addr) {
+#if defined(__aarch64__)
+ char tag;
+ iovec iov = {&tag, 1};
+ if (ptrace(PTRACE_PEEKMTETAGS, pid_, reinterpret_cast<void*>(addr), &iov) != 0 ||
+ iov.iov_len != 1) {
+ return -1;
+ }
+ return tag;
+#else
+ (void)addr;
+ return -1;
+#endif
+}
+
+long MemoryLocal::ReadTag(uint64_t addr) {
+#if defined(__aarch64__)
+ // Check that the memory is readable first. This is racy with the ldg but there's not much
+ // we can do about it.
+ char data;
+ if (!mte_supported() || !Read(addr, &data, 1)) {
+ return -1;
+ }
+
+ __asm__ __volatile__(".arch_extension mte; ldg %0, [%0]" : "+r"(addr) : : "memory");
+ return (addr >> 56) & 0xf;
+#else
+ (void)addr;
+ return -1;
+#endif
+}
+
+} // namespace unwindstack
+
+#endif
diff --git a/libunwindstack/MemoryRemote.h b/libunwindstack/MemoryRemote.h
index db367d6..dd09c88 100644
--- a/libunwindstack/MemoryRemote.h
+++ b/libunwindstack/MemoryRemote.h
@@ -32,6 +32,7 @@
virtual ~MemoryRemote() = default;
size_t Read(uint64_t addr, void* dst, size_t size) override;
+ long ReadTag(uint64_t addr) override;
pid_t pid() { return pid_; }
diff --git a/libunwindstack/Regs.cpp b/libunwindstack/Regs.cpp
index c7dec52..03aa6c2 100644
--- a/libunwindstack/Regs.cpp
+++ b/libunwindstack/Regs.cpp
@@ -100,10 +100,6 @@
return ARCH_X86;
#elif defined(__x86_64__)
return ARCH_X86_64;
-#elif defined(__mips__) && !defined(__LP64__)
- return ARCH_MIPS;
-#elif defined(__mips__) && defined(__LP64__)
- return ARCH_MIPS64;
#else
abort();
#endif
@@ -119,14 +115,68 @@
regs = new RegsX86();
#elif defined(__x86_64__)
regs = new RegsX86_64();
-#elif defined(__mips__) && !defined(__LP64__)
- regs = new RegsMips();
-#elif defined(__mips__) && defined(__LP64__)
- regs = new RegsMips64();
#else
abort();
#endif
return regs;
}
+uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf, ArchEnum arch) {
+ switch (arch) {
+ case ARCH_ARM: {
+ if (!elf->valid()) {
+ return 2;
+ }
+
+ uint64_t load_bias = elf->GetLoadBias();
+ if (rel_pc < load_bias) {
+ if (rel_pc < 2) {
+ return 0;
+ }
+ return 2;
+ }
+ uint64_t adjusted_rel_pc = rel_pc - load_bias;
+ if (adjusted_rel_pc < 5) {
+ if (adjusted_rel_pc < 2) {
+ return 0;
+ }
+ return 2;
+ }
+
+ if (adjusted_rel_pc & 1) {
+ // This is a thumb instruction, it could be 2 or 4 bytes.
+ uint32_t value;
+ if (!elf->memory()->ReadFully(adjusted_rel_pc - 5, &value, sizeof(value)) ||
+ (value & 0xe000f000) != 0xe000f000) {
+ return 2;
+ }
+ }
+ return 4;
+ }
+ case ARCH_ARM64: {
+ if (rel_pc < 4) {
+ return 0;
+ }
+ return 4;
+ }
+ case ARCH_MIPS:
+ case ARCH_MIPS64: {
+ if (rel_pc < 8) {
+ return 0;
+ }
+ // For now, just assume no compact branches
+ return 8;
+ }
+ case ARCH_X86:
+ case ARCH_X86_64: {
+ if (rel_pc == 0) {
+ return 0;
+ }
+ return 1;
+ }
+ case ARCH_UNKNOWN:
+ return 0;
+ }
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/RegsArm.cpp b/libunwindstack/RegsArm.cpp
index 1b1f7eb..1aaa08f 100644
--- a/libunwindstack/RegsArm.cpp
+++ b/libunwindstack/RegsArm.cpp
@@ -51,37 +51,6 @@
regs_[ARM_REG_SP] = sp;
}
-uint64_t RegsArm::GetPcAdjustment(uint64_t rel_pc, Elf* elf) {
- if (!elf->valid()) {
- return 2;
- }
-
- uint64_t load_bias = elf->GetLoadBias();
- if (rel_pc < load_bias) {
- if (rel_pc < 2) {
- return 0;
- }
- return 2;
- }
- uint64_t adjusted_rel_pc = rel_pc - load_bias;
- if (adjusted_rel_pc < 5) {
- if (adjusted_rel_pc < 2) {
- return 0;
- }
- return 2;
- }
-
- if (adjusted_rel_pc & 1) {
- // This is a thumb instruction, it could be 2 or 4 bytes.
- uint32_t value;
- if (!elf->memory()->ReadFully(adjusted_rel_pc - 5, &value, sizeof(value)) ||
- (value & 0xe000f000) != 0xe000f000) {
- return 2;
- }
- }
- return 4;
-}
-
bool RegsArm::SetPcFromReturnAddress(Memory*) {
uint32_t lr = regs_[ARM_REG_LR];
if (regs_[ARM_REG_PC] == lr) {
diff --git a/libunwindstack/RegsArm64.cpp b/libunwindstack/RegsArm64.cpp
index 00b3367..5b7431a 100644
--- a/libunwindstack/RegsArm64.cpp
+++ b/libunwindstack/RegsArm64.cpp
@@ -52,13 +52,6 @@
regs_[ARM64_REG_SP] = sp;
}
-uint64_t RegsArm64::GetPcAdjustment(uint64_t rel_pc, Elf*) {
- if (rel_pc < 4) {
- return 0;
- }
- return 4;
-}
-
bool RegsArm64::SetPcFromReturnAddress(Memory*) {
uint64_t lr = regs_[ARM64_REG_LR];
if (regs_[ARM64_REG_PC] == lr) {
diff --git a/libunwindstack/RegsMips.cpp b/libunwindstack/RegsMips.cpp
index ebefe42..ab84691 100644
--- a/libunwindstack/RegsMips.cpp
+++ b/libunwindstack/RegsMips.cpp
@@ -52,14 +52,6 @@
regs_[MIPS_REG_SP] = static_cast<uint32_t>(sp);
}
-uint64_t RegsMips::GetPcAdjustment(uint64_t rel_pc, Elf*) {
- if (rel_pc < 8) {
- return 0;
- }
- // For now, just assume no compact branches
- return 8;
-}
-
bool RegsMips::SetPcFromReturnAddress(Memory*) {
uint32_t ra = regs_[MIPS_REG_RA];
if (regs_[MIPS_REG_PC] == ra) {
diff --git a/libunwindstack/RegsMips64.cpp b/libunwindstack/RegsMips64.cpp
index be2fd22..7f600d3 100644
--- a/libunwindstack/RegsMips64.cpp
+++ b/libunwindstack/RegsMips64.cpp
@@ -52,14 +52,6 @@
regs_[MIPS64_REG_SP] = sp;
}
-uint64_t RegsMips64::GetPcAdjustment(uint64_t rel_pc, Elf*) {
- if (rel_pc < 8) {
- return 0;
- }
- // For now, just assume no compact branches
- return 8;
-}
-
bool RegsMips64::SetPcFromReturnAddress(Memory*) {
uint64_t ra = regs_[MIPS64_REG_RA];
if (regs_[MIPS64_REG_PC] == ra) {
diff --git a/libunwindstack/RegsX86.cpp b/libunwindstack/RegsX86.cpp
index 5538fc0..4d3c246 100644
--- a/libunwindstack/RegsX86.cpp
+++ b/libunwindstack/RegsX86.cpp
@@ -50,13 +50,6 @@
regs_[X86_REG_SP] = static_cast<uint32_t>(sp);
}
-uint64_t RegsX86::GetPcAdjustment(uint64_t rel_pc, Elf*) {
- if (rel_pc == 0) {
- return 0;
- }
- return 1;
-}
-
bool RegsX86::SetPcFromReturnAddress(Memory* process_memory) {
// Attempt to get the return address from the top of the stack.
uint32_t new_pc;
diff --git a/libunwindstack/RegsX86_64.cpp b/libunwindstack/RegsX86_64.cpp
index 5b9aa58..c9e245d 100644
--- a/libunwindstack/RegsX86_64.cpp
+++ b/libunwindstack/RegsX86_64.cpp
@@ -51,13 +51,6 @@
regs_[X86_64_REG_SP] = sp;
}
-uint64_t RegsX86_64::GetPcAdjustment(uint64_t rel_pc, Elf*) {
- if (rel_pc == 0) {
- return 0;
- }
- return 1;
-}
-
bool RegsX86_64::SetPcFromReturnAddress(Memory* process_memory) {
// Attempt to get the return address from the top of the stack.
uint64_t new_pc;
diff --git a/libunwindstack/Symbols.cpp b/libunwindstack/Symbols.cpp
index e3c15a2..2117ebd 100644
--- a/libunwindstack/Symbols.cpp
+++ b/libunwindstack/Symbols.cpp
@@ -19,6 +19,7 @@
#include <algorithm>
#include <string>
+#include <vector>
#include <unwindstack/Memory.h>
@@ -29,23 +30,55 @@
Symbols::Symbols(uint64_t offset, uint64_t size, uint64_t entry_size, uint64_t str_offset,
uint64_t str_size)
- : cur_offset_(offset),
- offset_(offset),
- end_(offset + size),
+ : offset_(offset),
+ count_(entry_size != 0 ? size / entry_size : 0),
entry_size_(entry_size),
str_offset_(str_offset),
str_end_(str_offset_ + str_size) {}
-const Symbols::Info* Symbols::GetInfoFromCache(uint64_t addr) {
- // Binary search the table.
+template <typename SymType>
+static bool IsFunc(const SymType* entry) {
+ return entry->st_shndx != SHN_UNDEF && ELF32_ST_TYPE(entry->st_info) == STT_FUNC;
+}
+
+// Read symbol entry from memory and cache it so we don't have to read it again.
+template <typename SymType>
+inline __attribute__((__always_inline__)) const Symbols::Info* Symbols::ReadFuncInfo(
+ uint32_t symbol_index, Memory* elf_memory) {
+ auto it = symbols_.find(symbol_index);
+ if (it != symbols_.end()) {
+ return &it->second;
+ }
+ SymType sym;
+ if (!elf_memory->ReadFully(offset_ + symbol_index * entry_size_, &sym, sizeof(sym))) {
+ return nullptr;
+ }
+ if (!IsFunc(&sym)) {
+ // We need the address for binary search, but we don't want it to be matched.
+ sym.st_size = 0;
+ }
+ Info info{.addr = sym.st_value, .size = static_cast<uint32_t>(sym.st_size), .name = sym.st_name};
+ return &symbols_.emplace(symbol_index, info).first->second;
+}
+
+// Binary search the symbol table to find function containing the given address.
+// Without remap, the symbol table is assumed to be sorted and accessed directly.
+// If the symbol table is not sorted this method might fail but should not crash.
+// When the indices are remapped, they are guaranteed to be sorted by address.
+template <typename SymType, bool RemapIndices>
+const Symbols::Info* Symbols::BinarySearch(uint64_t addr, Memory* elf_memory) {
size_t first = 0;
- size_t last = symbols_.size();
+ size_t last = RemapIndices ? remap_->size() : count_;
while (first < last) {
size_t current = first + (last - first) / 2;
- const Info* info = &symbols_[current];
- if (addr < info->start_offset) {
+ size_t symbol_index = RemapIndices ? remap_.value()[current] : current;
+ const Info* info = ReadFuncInfo<SymType>(symbol_index, elf_memory);
+ if (info == nullptr) {
+ return nullptr;
+ }
+ if (addr < info->addr) {
last = current;
- } else if (addr < info->end_offset) {
+ } else if (addr < info->addr + info->size) {
return info;
} else {
first = current + 1;
@@ -54,64 +87,72 @@
return nullptr;
}
+// Create remapping table which allows us to access symbols as if they were sorted by address.
template <typename SymType>
-bool Symbols::GetName(uint64_t addr, Memory* elf_memory, std::string* name, uint64_t* func_offset) {
- if (symbols_.size() != 0) {
- const Info* info = GetInfoFromCache(addr);
- if (info) {
- CHECK(addr >= info->start_offset && addr <= info->end_offset);
- *func_offset = addr - info->start_offset;
- return elf_memory->ReadString(info->str_offset, name, str_end_ - info->str_offset);
+void Symbols::BuildRemapTable(Memory* elf_memory) {
+ std::vector<uint64_t> addrs; // Addresses of all symbols (addrs[i] == symbols[i].st_value).
+ addrs.reserve(count_);
+ remap_.emplace(); // Construct the optional remap table.
+ remap_->reserve(count_);
+ for (size_t symbol_idx = 0; symbol_idx < count_;) {
+ // Read symbols from memory. We intentionally bypass the cache to save memory.
+ // Do the reads in batches so that we minimize the number of memory read calls.
+ uint8_t buffer[1024];
+ size_t read = std::min<size_t>(sizeof(buffer), (count_ - symbol_idx) * entry_size_);
+ size_t size = elf_memory->Read(offset_ + symbol_idx * entry_size_, buffer, read);
+ if (size < sizeof(SymType)) {
+ break; // Stop processing, something looks like it is corrupted.
}
- }
-
- bool symbol_added = false;
- bool return_value = false;
- while (cur_offset_ + entry_size_ <= end_) {
- SymType entry;
- if (!elf_memory->ReadFully(cur_offset_, &entry, sizeof(entry))) {
- // Stop all processing, something looks like it is corrupted.
- cur_offset_ = UINT64_MAX;
- return false;
- }
- cur_offset_ += entry_size_;
-
- if (entry.st_shndx != SHN_UNDEF && ELF32_ST_TYPE(entry.st_info) == STT_FUNC) {
- // Treat st_value as virtual address.
- uint64_t start_offset = entry.st_value;
- uint64_t end_offset = start_offset + entry.st_size;
-
- // Cache the value.
- symbols_.emplace_back(start_offset, end_offset, str_offset_ + entry.st_name);
- symbol_added = true;
-
- if (addr >= start_offset && addr < end_offset) {
- *func_offset = addr - start_offset;
- uint64_t offset = str_offset_ + entry.st_name;
- if (offset < str_end_) {
- return_value = elf_memory->ReadString(offset, name, str_end_ - offset);
- }
- break;
+ for (size_t offset = 0; offset + sizeof(SymType) <= size; offset += entry_size_, symbol_idx++) {
+ SymType sym;
+ memcpy(&sym, &buffer[offset], sizeof(SymType)); // Copy to ensure alignment.
+ addrs.push_back(sym.st_value); // Always insert so it is indexable by symbol index.
+ if (IsFunc(&sym)) {
+ remap_->push_back(symbol_idx); // Indices of function symbols only.
}
}
}
+ // Sort by address to make the remap list binary searchable (stable due to the a<b tie break).
+ auto comp = [&addrs](auto a, auto b) { return std::tie(addrs[a], a) < std::tie(addrs[b], b); };
+ std::sort(remap_->begin(), remap_->end(), comp);
+ // Remove duplicate entries (methods de-duplicated by the linker).
+ auto pred = [&addrs](auto a, auto b) { return addrs[a] == addrs[b]; };
+ remap_->erase(std::unique(remap_->begin(), remap_->end(), pred), remap_->end());
+ remap_->shrink_to_fit();
+}
- if (symbol_added) {
- std::sort(symbols_.begin(), symbols_.end(),
- [](const Info& a, const Info& b) { return a.start_offset < b.start_offset; });
+template <typename SymType>
+bool Symbols::GetName(uint64_t addr, Memory* elf_memory, std::string* name, uint64_t* func_offset) {
+ const Info* info;
+ if (!remap_.has_value()) {
+ // Assume the symbol table is sorted. If it is not, this will gracefully fail.
+ info = BinarySearch<SymType, false>(addr, elf_memory);
+ if (info == nullptr) {
+ // Create the remapping table and retry the search.
+ BuildRemapTable<SymType>(elf_memory);
+ symbols_.clear(); // Remove cached symbols since the access pattern will be different.
+ info = BinarySearch<SymType, true>(addr, elf_memory);
+ }
+ } else {
+ // Fast search using the previously created remap table.
+ info = BinarySearch<SymType, true>(addr, elf_memory);
}
- return return_value;
+ if (info == nullptr) {
+ return false;
+ }
+ // Read the function name from the string table.
+ *func_offset = addr - info->addr;
+ uint64_t str = str_offset_ + info->name;
+ return str < str_end_ && elf_memory->ReadString(str, name, str_end_ - str);
}
template <typename SymType>
bool Symbols::GetGlobal(Memory* elf_memory, const std::string& name, uint64_t* memory_address) {
- uint64_t cur_offset = offset_;
- while (cur_offset + entry_size_ <= end_) {
+ for (uint32_t i = 0; i < count_; i++) {
SymType entry;
- if (!elf_memory->ReadFully(cur_offset, &entry, sizeof(entry))) {
+ if (!elf_memory->ReadFully(offset_ + i * entry_size_, &entry, sizeof(entry))) {
return false;
}
- cur_offset += entry_size_;
if (entry.st_shndx != SHN_UNDEF && ELF32_ST_TYPE(entry.st_info) == STT_OBJECT &&
ELF32_ST_BIND(entry.st_info) == STB_GLOBAL) {
diff --git a/libunwindstack/Symbols.h b/libunwindstack/Symbols.h
index 7fcd067..3b3f20b 100644
--- a/libunwindstack/Symbols.h
+++ b/libunwindstack/Symbols.h
@@ -19,8 +19,9 @@
#include <stdint.h>
+#include <optional>
#include <string>
-#include <vector>
+#include <unordered_map>
namespace unwindstack {
@@ -29,11 +30,9 @@
class Symbols {
struct Info {
- Info(uint64_t start_offset, uint64_t end_offset, uint64_t str_offset)
- : start_offset(start_offset), end_offset(end_offset), str_offset(str_offset) {}
- uint64_t start_offset;
- uint64_t end_offset;
- uint64_t str_offset;
+ uint64_t addr; // Symbol address.
+ uint32_t size; // Symbol size in bytes. Zero if not a function.
+ uint32_t name; // Offset in .strtab.
};
public:
@@ -41,8 +40,6 @@
uint64_t str_size);
virtual ~Symbols() = default;
- const Info* GetInfoFromCache(uint64_t addr);
-
template <typename SymType>
bool GetName(uint64_t addr, Memory* elf_memory, std::string* name, uint64_t* func_offset);
@@ -51,18 +48,27 @@
void ClearCache() {
symbols_.clear();
- cur_offset_ = offset_;
+ remap_.reset();
}
private:
- uint64_t cur_offset_;
- uint64_t offset_;
- uint64_t end_;
- uint64_t entry_size_;
- uint64_t str_offset_;
- uint64_t str_end_;
+ template <typename SymType>
+ const Info* ReadFuncInfo(uint32_t symbol_index, Memory* elf_memory);
- std::vector<Info> symbols_;
+ template <typename SymType, bool RemapIndices>
+ const Info* BinarySearch(uint64_t addr, Memory* elf_memory);
+
+ template <typename SymType>
+ void BuildRemapTable(Memory* elf_memory);
+
+ const uint64_t offset_;
+ const uint64_t count_;
+ const uint64_t entry_size_;
+ const uint64_t str_offset_;
+ const uint64_t str_end_;
+
+ std::unordered_map<uint32_t, Info> symbols_; // Cache of read symbols (keyed by symbol index).
+ std::optional<std::vector<uint32_t>> remap_; // Indices of function symbols sorted by address.
};
} // namespace unwindstack
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 1bb0319..2d867cd 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -181,7 +181,7 @@
step_pc = rel_pc;
}
if (adjust_pc) {
- pc_adjustment = regs_->GetPcAdjustment(rel_pc, elf);
+ pc_adjustment = GetPcAdjustment(rel_pc, elf, arch);
} else {
pc_adjustment = 0;
}
@@ -395,4 +395,54 @@
return true;
}
+FrameData Unwinder::BuildFrameFromPcOnly(uint64_t pc) {
+ FrameData frame;
+
+ Maps* maps = GetMaps();
+ MapInfo* map_info = maps->Find(pc);
+ if (!map_info) {
+ frame.rel_pc = pc;
+ return frame;
+ }
+
+ ArchEnum arch = Regs::CurrentArch();
+ Elf* elf = map_info->GetElf(GetProcessMemory(), arch);
+
+ uint64_t relative_pc = elf->GetRelPc(pc, map_info);
+
+ uint64_t pc_adjustment = GetPcAdjustment(relative_pc, elf, arch);
+ relative_pc -= pc_adjustment;
+ // The debug PC may be different if the PC comes from the JIT.
+ uint64_t debug_pc = relative_pc;
+
+ // If we don't have a valid ELF file, check the JIT.
+ if (!elf->valid()) {
+ JitDebug jit_debug(GetProcessMemory());
+ uint64_t jit_pc = pc - pc_adjustment;
+ Elf* jit_elf = jit_debug.GetElf(maps, jit_pc);
+ if (jit_elf != nullptr) {
+ debug_pc = jit_pc;
+ elf = jit_elf;
+ }
+ }
+
+ // Copy all the things we need into the frame for symbolization.
+ frame.rel_pc = relative_pc;
+ frame.pc = pc - pc_adjustment;
+ frame.map_name = map_info->name;
+ frame.map_elf_start_offset = map_info->elf_start_offset;
+ frame.map_exact_offset = map_info->offset;
+ frame.map_start = map_info->start;
+ frame.map_end = map_info->end;
+ frame.map_flags = map_info->flags;
+ frame.map_load_bias = elf->GetLoadBias();
+
+ if (!resolve_names_ ||
+ !elf->GetFunctionName(relative_pc, &frame.function_name, &frame.function_offset)) {
+ frame.function_name = "";
+ frame.function_offset = 0;
+ }
+ return frame;
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/benchmarks/ElfBenchmark.cpp b/libunwindstack/benchmarks/ElfBenchmark.cpp
new file mode 100644
index 0000000..a46bd7a
--- /dev/null
+++ b/libunwindstack/benchmarks/ElfBenchmark.cpp
@@ -0,0 +1,142 @@
+/*
+ * 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 <err.h>
+#include <malloc.h>
+#include <stdint.h>
+
+#include <string>
+
+#include <benchmark/benchmark.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
+#include "Utils.h"
+
+static void BenchmarkElfCreate(benchmark::State& state, const std::string& elf_file) {
+#if defined(__BIONIC__)
+ uint64_t rss_bytes = 0;
+#endif
+ uint64_t alloc_bytes = 0;
+ for (auto _ : state) {
+ state.PauseTiming();
+#if defined(__BIONIC__)
+ mallopt(M_PURGE, 0);
+ uint64_t rss_bytes_before = 0;
+ GatherRss(&rss_bytes_before);
+#endif
+ uint64_t alloc_bytes_before = mallinfo().uordblks;
+ auto file_memory = unwindstack::Memory::CreateFileMemory(elf_file, 0);
+ state.ResumeTiming();
+
+ unwindstack::Elf elf(file_memory.release());
+ if (!elf.Init() || !elf.valid()) {
+ errx(1, "Internal Error: Cannot open elf.");
+ }
+
+ state.PauseTiming();
+#if defined(__BIONIC__)
+ mallopt(M_PURGE, 0);
+#endif
+ alloc_bytes += mallinfo().uordblks - alloc_bytes_before;
+#if defined(__BIONIC__)
+ GatherRss(&rss_bytes);
+ rss_bytes -= rss_bytes_before;
+#endif
+ state.ResumeTiming();
+ }
+
+#if defined(__BIONIC__)
+ state.counters["RSS_BYTES"] = rss_bytes / static_cast<double>(state.iterations());
+#endif
+ state.counters["ALLOCATED_BYTES"] = alloc_bytes / static_cast<double>(state.iterations());
+}
+
+void BM_elf_create(benchmark::State& state) {
+ BenchmarkElfCreate(state, GetElfFile());
+}
+BENCHMARK(BM_elf_create);
+
+void BM_elf_create_compressed(benchmark::State& state) {
+ BenchmarkElfCreate(state, GetCompressedElfFile());
+}
+BENCHMARK(BM_elf_create_compressed);
+
+static void InitializeBuildId(benchmark::State& state, unwindstack::Maps& maps,
+ unwindstack::MapInfo** build_id_map_info) {
+ if (!maps.Parse()) {
+ state.SkipWithError("Failed to parse local maps.");
+ return;
+ }
+
+ // Find the libc.so share library and use that for benchmark purposes.
+ *build_id_map_info = nullptr;
+ for (auto& map_info : maps) {
+ if (map_info->offset == 0 && map_info->GetBuildID() != "") {
+ *build_id_map_info = map_info.get();
+ break;
+ }
+ }
+
+ if (*build_id_map_info == nullptr) {
+ state.SkipWithError("Failed to find a map with a BuildID.");
+ }
+}
+
+static void BM_elf_get_build_id_from_object(benchmark::State& state) {
+ unwindstack::LocalMaps maps;
+ unwindstack::MapInfo* build_id_map_info;
+ InitializeBuildId(state, maps, &build_id_map_info);
+
+ unwindstack::Elf* elf = build_id_map_info->GetElf(std::shared_ptr<unwindstack::Memory>(),
+ unwindstack::Regs::CurrentArch());
+ if (!elf->valid()) {
+ state.SkipWithError("Cannot get valid elf from map.");
+ }
+
+ for (auto _ : state) {
+ state.PauseTiming();
+ uintptr_t id = build_id_map_info->build_id;
+ if (id != 0) {
+ delete reinterpret_cast<std::string*>(id);
+ build_id_map_info->build_id = 0;
+ }
+ state.ResumeTiming();
+ benchmark::DoNotOptimize(build_id_map_info->GetBuildID());
+ }
+}
+BENCHMARK(BM_elf_get_build_id_from_object);
+
+static void BM_elf_get_build_id_from_file(benchmark::State& state) {
+ unwindstack::LocalMaps maps;
+ unwindstack::MapInfo* build_id_map_info;
+ InitializeBuildId(state, maps, &build_id_map_info);
+
+ for (auto _ : state) {
+ state.PauseTiming();
+ uintptr_t id = build_id_map_info->build_id;
+ if (id != 0) {
+ delete reinterpret_cast<std::string*>(id);
+ build_id_map_info->build_id = 0;
+ }
+ state.ResumeTiming();
+ benchmark::DoNotOptimize(build_id_map_info->GetBuildID());
+ }
+}
+BENCHMARK(BM_elf_get_build_id_from_file);
diff --git a/libunwindstack/benchmarks/MapsBenchmark.cpp b/libunwindstack/benchmarks/MapsBenchmark.cpp
new file mode 100644
index 0000000..5df1491
--- /dev/null
+++ b/libunwindstack/benchmarks/MapsBenchmark.cpp
@@ -0,0 +1,160 @@
+/*
+ * 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 <err.h>
+#include <stdint.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+
+#include <benchmark/benchmark.h>
+
+#include <unwindstack/Maps.h>
+
+class BenchmarkLocalUpdatableMaps : public unwindstack::LocalUpdatableMaps {
+ public:
+ BenchmarkLocalUpdatableMaps() : unwindstack::LocalUpdatableMaps() {}
+ virtual ~BenchmarkLocalUpdatableMaps() = default;
+
+ const std::string GetMapsFile() const override { return maps_file_; }
+
+ void BenchmarkSetMapsFile(const std::string& maps_file) { maps_file_ = maps_file; }
+
+ private:
+ std::string maps_file_;
+};
+
+static constexpr size_t kNumSmallMaps = 100;
+static constexpr size_t kNumLargeMaps = 10000;
+
+static void CreateMap(const char* filename, size_t num_maps, size_t increment = 1) {
+ std::string maps;
+ for (size_t i = 0; i < num_maps; i += increment) {
+ maps += android::base::StringPrintf("%zu-%zu r-xp 0000 00:00 0 name%zu\n", i * 1000,
+ (i + 1) * 1000 * increment, i * increment);
+ }
+ if (!android::base::WriteStringToFile(maps, filename)) {
+ errx(1, "WriteStringToFile failed");
+ }
+}
+
+static void ReparseBenchmark(benchmark::State& state, const char* maps1, size_t maps1_total,
+ const char* maps2, size_t maps2_total) {
+ for (auto _ : state) {
+ BenchmarkLocalUpdatableMaps maps;
+ maps.BenchmarkSetMapsFile(maps1);
+ if (!maps.Reparse()) {
+ errx(1, "Internal Error: reparse of initial maps filed.");
+ }
+ if (maps.Total() != maps1_total) {
+ errx(1, "Internal Error: Incorrect total number of maps %zu, expected %zu.", maps.Total(),
+ maps1_total);
+ }
+ maps.BenchmarkSetMapsFile(maps2);
+ if (!maps.Reparse()) {
+ errx(1, "Internal Error: reparse of second set of maps filed.");
+ }
+ if (maps.Total() != maps2_total) {
+ errx(1, "Internal Error: Incorrect total number of maps %zu, expected %zu.", maps.Total(),
+ maps2_total);
+ }
+ }
+}
+
+void BM_local_updatable_maps_reparse_double_initial_small(benchmark::State& state) {
+ TemporaryFile initial_maps;
+ CreateMap(initial_maps.path, kNumSmallMaps, 2);
+
+ TemporaryFile reparse_maps;
+ CreateMap(reparse_maps.path, kNumSmallMaps);
+
+ ReparseBenchmark(state, initial_maps.path, kNumSmallMaps / 2, reparse_maps.path, kNumSmallMaps);
+}
+BENCHMARK(BM_local_updatable_maps_reparse_double_initial_small);
+
+void BM_local_updatable_maps_reparse_double_initial_large(benchmark::State& state) {
+ TemporaryFile initial_maps;
+ CreateMap(initial_maps.path, kNumLargeMaps, 2);
+
+ TemporaryFile reparse_maps;
+ CreateMap(reparse_maps.path, kNumLargeMaps);
+
+ ReparseBenchmark(state, initial_maps.path, kNumLargeMaps / 2, reparse_maps.path, kNumLargeMaps);
+}
+BENCHMARK(BM_local_updatable_maps_reparse_double_initial_large);
+
+void BM_local_updatable_maps_reparse_same_maps_small(benchmark::State& state) {
+ static constexpr size_t kNumSmallMaps = 100;
+ TemporaryFile maps;
+ CreateMap(maps.path, kNumSmallMaps);
+
+ ReparseBenchmark(state, maps.path, kNumSmallMaps, maps.path, kNumSmallMaps);
+}
+BENCHMARK(BM_local_updatable_maps_reparse_same_maps_small);
+
+void BM_local_updatable_maps_reparse_same_maps_large(benchmark::State& state) {
+ TemporaryFile maps;
+ CreateMap(maps.path, kNumLargeMaps);
+
+ ReparseBenchmark(state, maps.path, kNumLargeMaps, maps.path, kNumLargeMaps);
+}
+BENCHMARK(BM_local_updatable_maps_reparse_same_maps_large);
+
+void BM_local_updatable_maps_reparse_few_extra_small(benchmark::State& state) {
+ TemporaryFile maps1;
+ CreateMap(maps1.path, kNumSmallMaps - 4);
+
+ TemporaryFile maps2;
+ CreateMap(maps2.path, kNumSmallMaps);
+
+ ReparseBenchmark(state, maps1.path, kNumSmallMaps - 4, maps2.path, kNumSmallMaps);
+}
+BENCHMARK(BM_local_updatable_maps_reparse_few_extra_small);
+
+void BM_local_updatable_maps_reparse_few_extra_large(benchmark::State& state) {
+ TemporaryFile maps1;
+ CreateMap(maps1.path, kNumLargeMaps - 4);
+
+ TemporaryFile maps2;
+ CreateMap(maps2.path, kNumLargeMaps);
+
+ ReparseBenchmark(state, maps1.path, kNumLargeMaps - 4, maps2.path, kNumLargeMaps);
+}
+BENCHMARK(BM_local_updatable_maps_reparse_few_extra_large);
+
+void BM_local_updatable_maps_reparse_few_less_small(benchmark::State& state) {
+ TemporaryFile maps1;
+ CreateMap(maps1.path, kNumSmallMaps);
+
+ TemporaryFile maps2;
+ CreateMap(maps2.path, kNumSmallMaps - 4);
+
+ ReparseBenchmark(state, maps1.path, kNumSmallMaps, maps2.path, kNumSmallMaps - 4);
+}
+BENCHMARK(BM_local_updatable_maps_reparse_few_less_small);
+
+void BM_local_updatable_maps_reparse_few_less_large(benchmark::State& state) {
+ TemporaryFile maps1;
+ CreateMap(maps1.path, kNumLargeMaps);
+
+ TemporaryFile maps2;
+ CreateMap(maps2.path, kNumLargeMaps - 4);
+
+ ReparseBenchmark(state, maps1.path, kNumLargeMaps, maps2.path, kNumLargeMaps - 4);
+}
+BENCHMARK(BM_local_updatable_maps_reparse_few_less_large);
diff --git a/libunwindstack/benchmarks/SymbolBenchmark.cpp b/libunwindstack/benchmarks/SymbolBenchmark.cpp
new file mode 100644
index 0000000..73088da
--- /dev/null
+++ b/libunwindstack/benchmarks/SymbolBenchmark.cpp
@@ -0,0 +1,129 @@
+/*
+ * 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 <err.h>
+#include <inttypes.h>
+#include <malloc.h>
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include <benchmark/benchmark.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Memory.h>
+
+#include "Utils.h"
+
+static void BenchmarkSymbolLookup(benchmark::State& state, std::vector<uint64_t> offsets,
+ std::string elf_file, bool expect_found) {
+#if defined(__BIONIC__)
+ uint64_t rss_bytes = 0;
+#endif
+ uint64_t alloc_bytes = 0;
+ for (auto _ : state) {
+ state.PauseTiming();
+ unwindstack::Elf elf(unwindstack::Memory::CreateFileMemory(elf_file, 0).release());
+ if (!elf.Init() || !elf.valid()) {
+ errx(1, "Internal Error: Cannot open elf.");
+ }
+
+#if defined(__BIONIC__)
+ mallopt(M_PURGE, 0);
+ uint64_t rss_bytes_before = 0;
+ GatherRss(&rss_bytes_before);
+#endif
+ uint64_t alloc_bytes_before = mallinfo().uordblks;
+ state.ResumeTiming();
+
+ for (auto pc : offsets) {
+ std::string name;
+ uint64_t offset;
+ bool found = elf.GetFunctionName(pc, &name, &offset);
+ if (expect_found && !found) {
+ errx(1, "expected pc 0x%" PRIx64 " present, but not found.", pc);
+ } else if (!expect_found && found) {
+ errx(1, "expected pc 0x%" PRIx64 " not present, but found.", pc);
+ }
+ }
+
+ state.PauseTiming();
+#if defined(__BIONIC__)
+ mallopt(M_PURGE, 0);
+#endif
+ alloc_bytes += mallinfo().uordblks - alloc_bytes_before;
+#if defined(__BIONIC__)
+ GatherRss(&rss_bytes);
+ rss_bytes -= rss_bytes_before;
+#endif
+ state.ResumeTiming();
+ }
+
+#if defined(__BIONIC__)
+ state.counters["RSS_BYTES"] = rss_bytes / static_cast<double>(state.iterations());
+#endif
+ state.counters["ALLOCATED_BYTES"] = alloc_bytes / static_cast<double>(state.iterations());
+}
+
+static void BenchmarkSymbolLookup(benchmark::State& state, uint64_t pc, std::string elf_file,
+ bool expect_found) {
+ BenchmarkSymbolLookup(state, std::vector<uint64_t>{pc}, elf_file, expect_found);
+}
+
+void BM_symbol_not_present(benchmark::State& state) {
+ BenchmarkSymbolLookup(state, 0, GetElfFile(), false);
+}
+BENCHMARK(BM_symbol_not_present);
+
+void BM_symbol_find_single(benchmark::State& state) {
+ BenchmarkSymbolLookup(state, 0x22b2bc, GetElfFile(), true);
+}
+BENCHMARK(BM_symbol_find_single);
+
+void BM_symbol_find_single_many_times(benchmark::State& state) {
+ BenchmarkSymbolLookup(state, std::vector<uint64_t>(15, 0x22b2bc), GetElfFile(), true);
+}
+BENCHMARK(BM_symbol_find_single_many_times);
+
+void BM_symbol_find_multiple(benchmark::State& state) {
+ BenchmarkSymbolLookup(state,
+ std::vector<uint64_t>{0x22b2bc, 0xd5d30, 0x1312e8, 0x13582e, 0x1389c8},
+ GetElfFile(), true);
+}
+BENCHMARK(BM_symbol_find_multiple);
+
+void BM_symbol_not_present_from_sorted(benchmark::State& state) {
+ BenchmarkSymbolLookup(state, 0, GetSymbolSortedElfFile(), false);
+}
+BENCHMARK(BM_symbol_not_present_from_sorted);
+
+void BM_symbol_find_single_from_sorted(benchmark::State& state) {
+ BenchmarkSymbolLookup(state, 0x138638, GetSymbolSortedElfFile(), true);
+}
+BENCHMARK(BM_symbol_find_single_from_sorted);
+
+void BM_symbol_find_single_many_times_from_sorted(benchmark::State& state) {
+ BenchmarkSymbolLookup(state, std::vector<uint64_t>(15, 0x138638), GetSymbolSortedElfFile(), true);
+}
+BENCHMARK(BM_symbol_find_single_many_times_from_sorted);
+
+void BM_symbol_find_multiple_from_sorted(benchmark::State& state) {
+ BenchmarkSymbolLookup(state,
+ std::vector<uint64_t>{0x138638, 0x84350, 0x14df18, 0x1f3a38, 0x1f3ca8},
+ GetSymbolSortedElfFile(), true);
+}
+BENCHMARK(BM_symbol_find_multiple_from_sorted);
diff --git a/libunwindstack/benchmarks/Utils.cpp b/libunwindstack/benchmarks/Utils.cpp
new file mode 100644
index 0000000..c92f109
--- /dev/null
+++ b/libunwindstack/benchmarks/Utils.cpp
@@ -0,0 +1,62 @@
+/*
+ * 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 <err.h>
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/strings.h>
+#include <benchmark/benchmark.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Memory.h>
+
+std::string GetElfFile() {
+ return android::base::GetExecutableDirectory() + "/benchmarks/files/libart_arm.so";
+}
+
+std::string GetSymbolSortedElfFile() {
+ return android::base::GetExecutableDirectory() + "/benchmarks/files/boot_arm.oat";
+}
+
+std::string GetCompressedElfFile() {
+ // Both are the same right now.
+ return GetSymbolSortedElfFile();
+}
+
+#if defined(__BIONIC__)
+
+#include <meminfo/procmeminfo.h>
+#include <procinfo/process_map.h>
+
+void GatherRss(uint64_t* rss_bytes) {
+ android::meminfo::ProcMemInfo proc_mem(getpid());
+ const std::vector<android::meminfo::Vma>& maps = proc_mem.MapsWithoutUsageStats();
+ for (auto& vma : maps) {
+ if (vma.name == "[anon:libc_malloc]" || android::base::StartsWith(vma.name, "[anon:scudo:") ||
+ android::base::StartsWith(vma.name, "[anon:GWP-ASan")) {
+ android::meminfo::Vma update_vma(vma);
+ if (!proc_mem.FillInVmaStats(update_vma)) {
+ err(1, "FillInVmaStats failed\n");
+ }
+ *rss_bytes += update_vma.usage.rss;
+ }
+ }
+}
+#endif
diff --git a/init/service_lock.cpp b/libunwindstack/benchmarks/Utils.h
similarity index 61%
copy from init/service_lock.cpp
copy to libunwindstack/benchmarks/Utils.h
index 404d439..bee6efc 100644
--- a/init/service_lock.cpp
+++ b/libunwindstack/benchmarks/Utils.h
@@ -14,12 +14,26 @@
* limitations under the License.
*/
-#include "service_lock.h"
+#ifndef _LIBUNWINDSTACK_UTILS_H
+#define _LIBUNWINDSTACK_UTILS_H
-namespace android {
-namespace init {
+#include <stdint.h>
-RecursiveMutex service_lock;
+#include <string>
-} // namespace init
-} // namespace android
+std::string GetElfFile();
+
+std::string GetSymbolSortedElfFile();
+
+std::string GetCompressedElfFile();
+
+#if defined(__BIONIC__)
+
+#include <meminfo/procmeminfo.h>
+#include <procinfo/process_map.h>
+
+void GatherRss(uint64_t* rss_bytes);
+
+#endif
+
+#endif // _LIBUNWINDSTACK_UTILS_h
diff --git a/libunwindstack/benchmarks/files/boot_arm.oat b/libunwindstack/benchmarks/files/boot_arm.oat
new file mode 100644
index 0000000..51188eb
--- /dev/null
+++ b/libunwindstack/benchmarks/files/boot_arm.oat
Binary files differ
diff --git a/libunwindstack/benchmarks/files/libart_arm.so b/libunwindstack/benchmarks/files/libart_arm.so
new file mode 100644
index 0000000..2201faf
--- /dev/null
+++ b/libunwindstack/benchmarks/files/libart_arm.so
Binary files differ
diff --git a/libunwindstack/benchmarks/unwind_benchmarks.cpp b/libunwindstack/benchmarks/unwind_benchmarks.cpp
index de9137a..0bee6ef 100644
--- a/libunwindstack/benchmarks/unwind_benchmarks.cpp
+++ b/libunwindstack/benchmarks/unwind_benchmarks.cpp
@@ -22,7 +22,6 @@
#include <android-base/strings.h>
-#include <unwindstack/Elf.h>
#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
#include <unwindstack/Regs.h>
@@ -83,63 +82,4 @@
}
BENCHMARK(BM_cached_unwind);
-static void Initialize(benchmark::State& state, unwindstack::Maps& maps,
- unwindstack::MapInfo** build_id_map_info) {
- if (!maps.Parse()) {
- state.SkipWithError("Failed to parse local maps.");
- return;
- }
-
- // Find the libc.so share library and use that for benchmark purposes.
- *build_id_map_info = nullptr;
- for (auto& map_info : maps) {
- if (map_info->offset == 0 && map_info->GetBuildID() != "") {
- *build_id_map_info = map_info.get();
- break;
- }
- }
-
- if (*build_id_map_info == nullptr) {
- state.SkipWithError("Failed to find a map with a BuildID.");
- }
-}
-
-static void BM_get_build_id_from_elf(benchmark::State& state) {
- unwindstack::LocalMaps maps;
- unwindstack::MapInfo* build_id_map_info;
- Initialize(state, maps, &build_id_map_info);
-
- unwindstack::Elf* elf = build_id_map_info->GetElf(std::shared_ptr<unwindstack::Memory>(),
- unwindstack::Regs::CurrentArch());
- if (!elf->valid()) {
- state.SkipWithError("Cannot get valid elf from map.");
- }
-
- for (auto _ : state) {
- uintptr_t id = build_id_map_info->build_id;
- if (id != 0) {
- delete reinterpret_cast<std::string*>(id);
- build_id_map_info->build_id = 0;
- }
- benchmark::DoNotOptimize(build_id_map_info->GetBuildID());
- }
-}
-BENCHMARK(BM_get_build_id_from_elf);
-
-static void BM_get_build_id_from_file(benchmark::State& state) {
- unwindstack::LocalMaps maps;
- unwindstack::MapInfo* build_id_map_info;
- Initialize(state, maps, &build_id_map_info);
-
- for (auto _ : state) {
- uintptr_t id = build_id_map_info->build_id;
- if (id != 0) {
- delete reinterpret_cast<std::string*>(id);
- build_id_map_info->build_id = 0;
- }
- benchmark::DoNotOptimize(build_id_map_info->GetBuildID());
- }
-}
-BENCHMARK(BM_get_build_id_from_file);
-
BENCHMARK_MAIN();
diff --git a/libunwindstack/include/unwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h
index 3106564..3d81878 100644
--- a/libunwindstack/include/unwindstack/Memory.h
+++ b/libunwindstack/include/unwindstack/Memory.h
@@ -37,11 +37,14 @@
uint64_t end);
static std::unique_ptr<Memory> CreateFileMemory(const std::string& path, uint64_t offset);
- virtual bool ReadString(uint64_t addr, std::string* string, uint64_t max_read = UINT64_MAX);
+ virtual bool ReadString(uint64_t addr, std::string* dst, size_t max_read);
virtual void Clear() {}
+ virtual bool IsLocal() const { return false; }
+
virtual size_t Read(uint64_t addr, void* dst, size_t size) = 0;
+ virtual long ReadTag(uint64_t) { return -1; }
bool ReadFully(uint64_t addr, void* dst, size_t size);
diff --git a/libunwindstack/include/unwindstack/Regs.h b/libunwindstack/include/unwindstack/Regs.h
index 4f761b4..a367e6c 100644
--- a/libunwindstack/include/unwindstack/Regs.h
+++ b/libunwindstack/include/unwindstack/Regs.h
@@ -64,8 +64,6 @@
uint64_t dex_pc() { return dex_pc_; }
void set_dex_pc(uint64_t dex_pc) { dex_pc_ = dex_pc; }
- virtual uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) = 0;
-
virtual bool StepIfSignalHandler(uint64_t elf_offset, Elf* elf, Memory* process_memory) = 0;
virtual bool SetPcFromReturnAddress(Memory* process_memory) = 0;
@@ -110,6 +108,8 @@
std::vector<AddressType> regs_;
};
+uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf, ArchEnum arch);
+
} // namespace unwindstack
#endif // _LIBUNWINDSTACK_REGS_H
diff --git a/libunwindstack/include/unwindstack/RegsArm.h b/libunwindstack/include/unwindstack/RegsArm.h
index aa029be..fbb34e7 100644
--- a/libunwindstack/include/unwindstack/RegsArm.h
+++ b/libunwindstack/include/unwindstack/RegsArm.h
@@ -36,8 +36,6 @@
ArchEnum Arch() override final;
- uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) override;
-
bool SetPcFromReturnAddress(Memory* process_memory) override;
bool StepIfSignalHandler(uint64_t elf_offset, Elf* elf, Memory* process_memory) override;
diff --git a/libunwindstack/include/unwindstack/RegsArm64.h b/libunwindstack/include/unwindstack/RegsArm64.h
index 5cd7e5b..2b3ddeb 100644
--- a/libunwindstack/include/unwindstack/RegsArm64.h
+++ b/libunwindstack/include/unwindstack/RegsArm64.h
@@ -36,8 +36,6 @@
ArchEnum Arch() override final;
- uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) override;
-
bool SetPcFromReturnAddress(Memory* process_memory) override;
bool StepIfSignalHandler(uint64_t elf_offset, Elf* elf, Memory* process_memory) override;
diff --git a/libunwindstack/include/unwindstack/RegsGetLocal.h b/libunwindstack/include/unwindstack/RegsGetLocal.h
index f0b5e3a..300a3ec 100644
--- a/libunwindstack/include/unwindstack/RegsGetLocal.h
+++ b/libunwindstack/include/unwindstack/RegsGetLocal.h
@@ -81,7 +81,7 @@
: "x12", "x13", "memory");
}
-#elif defined(__i386__) || defined(__x86_64__) || defined(__mips__)
+#elif defined(__i386__) || defined(__x86_64__)
extern "C" void AsmGetRegs(void* regs);
diff --git a/libunwindstack/include/unwindstack/RegsMips.h b/libunwindstack/include/unwindstack/RegsMips.h
index 8164a15..dc09b83 100644
--- a/libunwindstack/include/unwindstack/RegsMips.h
+++ b/libunwindstack/include/unwindstack/RegsMips.h
@@ -36,8 +36,6 @@
ArchEnum Arch() override final;
- uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) override;
-
bool SetPcFromReturnAddress(Memory* process_memory) override;
bool StepIfSignalHandler(uint64_t elf_offset, Elf* elf, Memory* process_memory) override;
diff --git a/libunwindstack/include/unwindstack/RegsMips64.h b/libunwindstack/include/unwindstack/RegsMips64.h
index c982542..64a80dc 100644
--- a/libunwindstack/include/unwindstack/RegsMips64.h
+++ b/libunwindstack/include/unwindstack/RegsMips64.h
@@ -36,8 +36,6 @@
ArchEnum Arch() override final;
- uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) override;
-
bool SetPcFromReturnAddress(Memory* process_memory) override;
bool StepIfSignalHandler(uint64_t elf_offset, Elf* elf, Memory* process_memory) override;
diff --git a/libunwindstack/include/unwindstack/RegsX86.h b/libunwindstack/include/unwindstack/RegsX86.h
index 2323a4f..cfbdda6 100644
--- a/libunwindstack/include/unwindstack/RegsX86.h
+++ b/libunwindstack/include/unwindstack/RegsX86.h
@@ -37,8 +37,6 @@
ArchEnum Arch() override final;
- uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) override;
-
bool SetPcFromReturnAddress(Memory* process_memory) override;
bool StepIfSignalHandler(uint64_t elf_offset, Elf* elf, Memory* process_memory) override;
diff --git a/libunwindstack/include/unwindstack/RegsX86_64.h b/libunwindstack/include/unwindstack/RegsX86_64.h
index 3e919a4..a11aef0 100644
--- a/libunwindstack/include/unwindstack/RegsX86_64.h
+++ b/libunwindstack/include/unwindstack/RegsX86_64.h
@@ -37,8 +37,6 @@
ArchEnum Arch() override final;
- uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) override;
-
bool SetPcFromReturnAddress(Memory* process_memory) override;
bool StepIfSignalHandler(uint64_t elf_offset, Elf* elf, Memory* process_memory) override;
diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h
index 67762c0..4d49f23 100644
--- a/libunwindstack/include/unwindstack/Unwinder.h
+++ b/libunwindstack/include/unwindstack/Unwinder.h
@@ -114,6 +114,13 @@
ErrorCode LastErrorCode() { return last_error_.code; }
uint64_t LastErrorAddress() { return last_error_.address; }
+ // Builds a frame for symbolization using the maps from this unwinder. The
+ // constructed frame contains just enough information to be used to symbolize
+ // frames collected by frame-pointer unwinding that's done outside of
+ // libunwindstack. This is used by tombstoned to symbolize frame pointer-based
+ // stack traces that are collected by tools such as GWP-ASan and MTE.
+ FrameData BuildFrameFromPcOnly(uint64_t pc);
+
protected:
Unwinder(size_t max_frames) : max_frames_(max_frames) { frames_.reserve(max_frames); }
diff --git a/libunwindstack/tests/DexFileTest.cpp b/libunwindstack/tests/DexFileTest.cpp
index 1b54da6..1deba01 100644
--- a/libunwindstack/tests/DexFileTest.cpp
+++ b/libunwindstack/tests/DexFileTest.cpp
@@ -14,12 +14,14 @@
* limitations under the License.
*/
+#include <malloc.h>
#include <stdint.h>
#include <sys/types.h>
#include <unistd.h>
#include <unordered_map>
+#include <MemoryLocal.h>
#include <android-base/file.h>
#include <gtest/gtest.h>
#include <unwindstack/MapInfo.h>
@@ -72,12 +74,43 @@
EXPECT_TRUE(DexFileFromFile::Create(0x100, tf.path) != nullptr);
}
+static constexpr size_t kNumLeakLoops = 5000;
+static constexpr size_t kMaxAllowedLeakBytes = 1024;
+
+static void CheckForLeak(size_t loop, size_t* first_allocated_bytes, size_t* last_allocated_bytes) {
+ size_t allocated_bytes = mallinfo().uordblks;
+ if (*first_allocated_bytes == 0) {
+ *first_allocated_bytes = allocated_bytes;
+ } else if (*last_allocated_bytes > *first_allocated_bytes) {
+ // Check that the total memory did not increase too much over the first loop.
+ ASSERT_LE(*last_allocated_bytes - *first_allocated_bytes, kMaxAllowedLeakBytes)
+ << "Failed in loop " << loop << " first_allocated_bytes " << *first_allocated_bytes
+ << " last_allocated_bytes " << *last_allocated_bytes;
+ }
+ *last_allocated_bytes = allocated_bytes;
+}
+
+TEST(DexFileTest, from_file_no_leak) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+
+ ASSERT_EQ(sizeof(kDexData),
+ static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
+
+ size_t first_allocated_bytes = 0;
+ size_t last_allocated_bytes = 0;
+ for (size_t i = 0; i < kNumLeakLoops; i++) {
+ EXPECT_TRUE(DexFileFromFile::Create(0, tf.path) != nullptr);
+ ASSERT_NO_FATAL_FAILURE(CheckForLeak(i, &first_allocated_bytes, &last_allocated_bytes));
+ }
+}
+
TEST(DexFileTest, from_memory_fail_too_small_for_header) {
MemoryFake memory;
memory.SetMemory(0x1000, kDexData, 10);
- EXPECT_TRUE(DexFileFromMemory::Create(0x1000, &memory, "") == nullptr);
+ EXPECT_TRUE(DexFileFromMemory::Create(0x1000, &memory, "", sizeof(kDexData)) == nullptr);
}
TEST(DexFileTest, from_memory_fail_too_small_for_data) {
@@ -85,7 +118,7 @@
memory.SetMemory(0x1000, kDexData, sizeof(kDexData) - 2);
- EXPECT_TRUE(DexFileFromMemory::Create(0x1000, &memory, "") == nullptr);
+ EXPECT_TRUE(DexFileFromMemory::Create(0x1000, &memory, "", sizeof(kDexData)) == nullptr);
}
TEST(DexFileTest, from_memory_open) {
@@ -93,7 +126,20 @@
memory.SetMemory(0x1000, kDexData, sizeof(kDexData));
- EXPECT_TRUE(DexFileFromMemory::Create(0x1000, &memory, "") != nullptr);
+ EXPECT_TRUE(DexFileFromMemory::Create(0x1000, &memory, "", sizeof(kDexData)) != nullptr);
+}
+
+TEST(DexFileTest, from_memory_no_leak) {
+ MemoryFake memory;
+
+ memory.SetMemory(0x1000, kDexData, sizeof(kDexData));
+
+ size_t first_allocated_bytes = 0;
+ size_t last_allocated_bytes = 0;
+ for (size_t i = 0; i < kNumLeakLoops; i++) {
+ EXPECT_TRUE(DexFileFromMemory::Create(0x1000, &memory, "", sizeof(kDexData)) != nullptr);
+ ASSERT_NO_FATAL_FAILURE(CheckForLeak(i, &first_allocated_bytes, &last_allocated_bytes));
+ }
}
TEST(DexFileTest, create_using_file) {
@@ -168,6 +214,43 @@
EXPECT_TRUE(dex_file == nullptr);
}
+TEST(DexFileTest, create_using_memory_size_too_small) {
+ MemoryFake memory;
+ memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
+ MapInfo info(nullptr, nullptr, 0x100, sizeof(kDexData) - 2, 0x200, 0x5, "/does/not/exist");
+ EXPECT_TRUE(DexFile::Create(0x4000, &memory, &info) != nullptr);
+}
+
+class MemoryLocalFake : public MemoryLocal {
+ public:
+ MemoryLocalFake(size_t memory_size) : backing_(memory_size) {}
+ virtual ~MemoryLocalFake() = default;
+
+ void* Data() { return backing_.data(); }
+
+ private:
+ std::vector<void*> backing_;
+};
+
+TEST(DexFileTest, create_using_local_memory) {
+ MemoryLocalFake memory(sizeof(kDexData));
+
+ memcpy(memory.Data(), kDexData, sizeof(kDexData));
+ uint64_t start = reinterpret_cast<uint64_t>(memory.Data());
+ MapInfo info(nullptr, nullptr, start, start + 0x1000, 0x200, 0x5, "/does/not/exist");
+ EXPECT_TRUE(DexFile::Create(start, &memory, &info) != nullptr);
+}
+
+TEST(DexFileTest, create_using_local_memory_size_too_small) {
+ MemoryLocalFake memory(sizeof(kDexData));
+
+ memcpy(memory.Data(), kDexData, sizeof(kDexData));
+ uint64_t start = reinterpret_cast<uint64_t>(memory.Data());
+ MapInfo info(nullptr, nullptr, start, start + sizeof(kDexData) - 2, 0x200, 0x5,
+ "/does/not/exist");
+ EXPECT_TRUE(DexFile::Create(start, &memory, &info) == nullptr);
+}
+
TEST(DexFileTest, get_method) {
MemoryFake memory;
memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
diff --git a/libunwindstack/tests/ElfFake.h b/libunwindstack/tests/ElfFake.h
index fc90dab..3b6cb80 100644
--- a/libunwindstack/tests/ElfFake.h
+++ b/libunwindstack/tests/ElfFake.h
@@ -55,6 +55,8 @@
void FakeSetLoadBias(uint64_t load_bias) { load_bias_ = load_bias; }
+ void FakeSetArch(ArchEnum arch) { arch_ = arch; }
+
void FakeSetInterface(ElfInterface* interface) { interface_.reset(interface); }
void FakeSetGnuDebugdataInterface(ElfInterface* interface) {
gnu_debugdata_interface_.reset(interface);
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index 1f3ed81..f0852a4 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -438,6 +438,48 @@
EXPECT_EQ(0xc080U, offset);
}
+TEST_F(ElfTest, get_global_vaddr_with_tagged_pointer) {
+ ElfFake elf(memory_);
+ elf.FakeSetValid(true);
+ elf.FakeSetArch(ARCH_ARM64);
+
+ ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+ elf.FakeSetInterface(interface);
+ interface->MockSetDataVaddrStart(0x500);
+ interface->MockSetDataVaddrEnd(0x600);
+ interface->MockSetDataOffset(0xa000);
+
+ std::string global("something");
+ EXPECT_CALL(*interface, GetGlobalVariable(global, ::testing::_))
+ .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x8800000000000580),
+ ::testing::Return(true)));
+
+ uint64_t offset;
+ ASSERT_TRUE(elf.GetGlobalVariableOffset(global, &offset));
+ EXPECT_EQ(0xa080U, offset);
+}
+
+TEST_F(ElfTest, get_global_vaddr_without_tagged_pointer) {
+ ElfFake elf(memory_);
+ elf.FakeSetValid(true);
+ elf.FakeSetArch(ARCH_X86_64);
+
+ ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+ elf.FakeSetInterface(interface);
+ interface->MockSetDataVaddrStart(0x8800000000000500);
+ interface->MockSetDataVaddrEnd(0x8800000000000600);
+ interface->MockSetDataOffset(0x880000000000a000);
+
+ std::string global("something");
+ EXPECT_CALL(*interface, GetGlobalVariable(global, ::testing::_))
+ .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x8800000000000580),
+ ::testing::Return(true)));
+
+ uint64_t offset;
+ ASSERT_TRUE(elf.GetGlobalVariableOffset(global, &offset));
+ EXPECT_EQ(0x880000000000a080U, offset);
+}
+
TEST_F(ElfTest, is_valid_pc_elf_invalid) {
ElfFake elf(memory_);
elf.FakeSetValid(false);
diff --git a/libunwindstack/tests/LocalUpdatableMapsTest.cpp b/libunwindstack/tests/LocalUpdatableMapsTest.cpp
index b816b9a..99afb0b 100644
--- a/libunwindstack/tests/LocalUpdatableMapsTest.cpp
+++ b/libunwindstack/tests/LocalUpdatableMapsTest.cpp
@@ -271,4 +271,103 @@
EXPECT_TRUE(map_info->name.empty());
}
+TEST_F(LocalUpdatableMapsTest, add_map_prev_name_updated) {
+ TemporaryFile tf;
+ ASSERT_TRUE(
+ android::base::WriteStringToFile("3000-4000 rwxp 00000 00:00 0\n"
+ "8000-9000 r-xp 00000 00:00 0\n"
+ "9000-a000 r-xp 00000 00:00 0\n",
+ tf.path));
+
+ maps_.TestSetMapsFile(tf.path);
+ ASSERT_TRUE(maps_.Reparse());
+ ASSERT_EQ(3U, maps_.Total());
+
+ MapInfo* map_info = maps_.Get(2);
+ ASSERT_TRUE(map_info != nullptr);
+ EXPECT_EQ(0x9000U, map_info->start);
+ EXPECT_EQ(0xA000U, map_info->end);
+ EXPECT_EQ(0U, map_info->offset);
+ EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+ EXPECT_TRUE(map_info->name.empty());
+ EXPECT_EQ(maps_.Get(1), map_info->prev_map);
+}
+
+TEST_F(LocalUpdatableMapsTest, add_map_prev_real_name_updated) {
+ TemporaryFile tf;
+ ASSERT_TRUE(
+ android::base::WriteStringToFile("3000-4000 r-xp 00000 00:00 0 /fake/lib.so\n"
+ "4000-5000 ---p 00000 00:00 0\n"
+ "7000-8000 r-xp 00000 00:00 0 /fake/lib1.so\n"
+ "8000-9000 ---p 00000 00:00 0\n",
+ tf.path));
+
+ maps_.TestSetMapsFile(tf.path);
+ ASSERT_TRUE(maps_.Reparse());
+ ASSERT_EQ(4U, maps_.Total());
+
+ MapInfo* map_info = maps_.Get(2);
+ ASSERT_TRUE(map_info != nullptr);
+ EXPECT_EQ(0x7000U, map_info->start);
+ EXPECT_EQ(0x8000U, map_info->end);
+ EXPECT_EQ(0U, map_info->offset);
+ EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+ EXPECT_EQ(maps_.Get(0), map_info->prev_real_map);
+ EXPECT_EQ(maps_.Get(1), map_info->prev_map);
+ EXPECT_EQ("/fake/lib1.so", map_info->name);
+
+ map_info = maps_.Get(3);
+ ASSERT_TRUE(map_info != nullptr);
+ EXPECT_EQ(0x8000U, map_info->start);
+ EXPECT_EQ(0x9000U, map_info->end);
+ EXPECT_EQ(0U, map_info->offset);
+ EXPECT_TRUE(map_info->IsBlank());
+ EXPECT_EQ(maps_.Get(2), map_info->prev_real_map);
+ EXPECT_EQ(maps_.Get(2), map_info->prev_map);
+ EXPECT_TRUE(map_info->name.empty());
+
+ ASSERT_TRUE(
+ android::base::WriteStringToFile("3000-4000 r-xp 00000 00:00 0 /fake/lib.so\n"
+ "4000-5000 ---p 00000 00:00 0\n"
+ "7000-8000 r-xp 00000 00:00 0 /fake/lib1.so\n"
+ "8000-9000 ---p 00000 00:00 0\n"
+ "9000-a000 r-xp 00000 00:00 0 /fake/lib2.so\n"
+ "a000-b000 r-xp 00000 00:00 0 /fake/lib3.so\n",
+ tf.path));
+
+ maps_.TestSetMapsFile(tf.path);
+ ASSERT_TRUE(maps_.Reparse());
+ ASSERT_EQ(6U, maps_.Total());
+
+ map_info = maps_.Get(2);
+ ASSERT_TRUE(map_info != nullptr);
+ EXPECT_EQ(0x7000U, map_info->start);
+ EXPECT_EQ(0x8000U, map_info->end);
+ EXPECT_EQ(0U, map_info->offset);
+ EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+ EXPECT_EQ("/fake/lib1.so", map_info->name);
+ EXPECT_EQ(maps_.Get(1), map_info->prev_map);
+ EXPECT_EQ(maps_.Get(0), map_info->prev_real_map);
+
+ map_info = maps_.Get(4);
+ ASSERT_TRUE(map_info != nullptr);
+ EXPECT_EQ(0x9000U, map_info->start);
+ EXPECT_EQ(0xA000U, map_info->end);
+ EXPECT_EQ(0U, map_info->offset);
+ EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+ EXPECT_EQ("/fake/lib2.so", map_info->name);
+ EXPECT_EQ(maps_.Get(3), map_info->prev_map);
+ EXPECT_EQ(maps_.Get(2), map_info->prev_real_map);
+
+ map_info = maps_.Get(5);
+ ASSERT_TRUE(map_info != nullptr);
+ EXPECT_EQ(0xA000U, map_info->start);
+ EXPECT_EQ(0xB000U, map_info->end);
+ EXPECT_EQ(0U, map_info->offset);
+ EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+ EXPECT_EQ("/fake/lib3.so", map_info->name);
+ EXPECT_EQ(maps_.Get(4), map_info->prev_map);
+ EXPECT_EQ(maps_.Get(4), map_info->prev_real_map);
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/MapInfoGetBuildIDTest.cpp b/libunwindstack/tests/MapInfoGetBuildIDTest.cpp
index 6953e26..70e136b 100644
--- a/libunwindstack/tests/MapInfoGetBuildIDTest.cpp
+++ b/libunwindstack/tests/MapInfoGetBuildIDTest.cpp
@@ -142,15 +142,14 @@
char note_section[128];
Elf32_Nhdr note_header = {};
- note_header.n_namesz = 4; // "GNU"
- note_header.n_descsz = 12; // "ELF_BUILDID"
+ note_header.n_namesz = sizeof("GNU");
+ note_header.n_descsz = sizeof("ELF_BUILDID") - 1;
note_header.n_type = NT_GNU_BUILD_ID;
memcpy(¬e_section, ¬e_header, sizeof(note_header));
size_t note_offset = sizeof(note_header);
- memcpy(¬e_section[note_offset], "GNU", sizeof("GNU"));
- note_offset += sizeof("GNU");
- memcpy(¬e_section[note_offset], "ELF_BUILDID", sizeof("ELF_BUILDID"));
- note_offset += sizeof("ELF_BUILDID");
+ memcpy(¬e_section[note_offset], "GNU", note_header.n_namesz);
+ note_offset += note_header.n_namesz;
+ memcpy(¬e_section[note_offset], "ELF_BUILDID", note_header.n_descsz);
Elf32_Shdr shdr = {};
shdr.sh_type = SHT_NOTE;
@@ -195,4 +194,10 @@
MultipleThreadTest("ELF_BUILDID");
}
+TEST_F(MapInfoGetBuildIDTest, real_elf) {
+ MapInfo map_info(nullptr, nullptr, 0x1000, 0x20000, 0, PROT_READ | PROT_WRITE,
+ TestGetFileDirectory() + "offline/empty_arm64/libc.so");
+ EXPECT_EQ("6df0590c4920f4c7b9f34fe833f37d54", map_info.GetPrintableBuildID());
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryMteTest.cpp b/libunwindstack/tests/MemoryMteTest.cpp
new file mode 100644
index 0000000..3ae322e
--- /dev/null
+++ b/libunwindstack/tests/MemoryMteTest.cpp
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ */
+
+#if defined(ANDROID_EXPERIMENTAL_MTE)
+
+#include <sys/mman.h>
+#include <sys/types.h>
+
+#include <gtest/gtest.h>
+
+#include <bionic/mte.h>
+
+#include "MemoryLocal.h"
+#include "MemoryRemote.h"
+#include "TestUtils.h"
+
+namespace unwindstack {
+
+static uintptr_t CreateTagMapping() {
+ uintptr_t mapping =
+ reinterpret_cast<uintptr_t>(mmap(nullptr, getpagesize(), PROT_READ | PROT_WRITE | PROT_MTE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
+ if (reinterpret_cast<void*>(mapping) == MAP_FAILED) {
+ return 0;
+ }
+#if defined(__aarch64__)
+ __asm__ __volatile__(".arch_extension mte; stg %0, [%0]"
+ :
+ : "r"(mapping + (1ULL << 56))
+ : "memory");
+#endif
+ return mapping;
+}
+
+TEST(MemoryMteTest, remote_read_tag) {
+#if !defined(__aarch64__)
+ GTEST_SKIP() << "Requires aarch64";
+#else
+ if (!mte_supported()) {
+ GTEST_SKIP() << "Requires MTE";
+ }
+#endif
+
+ uintptr_t mapping = CreateTagMapping();
+ ASSERT_NE(0U, mapping);
+
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ while (true)
+ ;
+ exit(1);
+ }
+ ASSERT_LT(0, pid);
+ TestScopedPidReaper reap(pid);
+
+ ASSERT_TRUE(TestAttach(pid));
+
+ MemoryRemote remote(pid);
+
+ EXPECT_EQ(1, remote.ReadTag(mapping));
+ EXPECT_EQ(0, remote.ReadTag(mapping + 16));
+
+ ASSERT_TRUE(TestDetach(pid));
+}
+
+TEST(MemoryMteTest, local_read_tag) {
+#if !defined(__aarch64__)
+ GTEST_SKIP() << "Requires aarch64";
+#else
+ if (!mte_supported()) {
+ GTEST_SKIP() << "Requires MTE";
+ }
+#endif
+
+ uintptr_t mapping = CreateTagMapping();
+ ASSERT_NE(0U, mapping);
+
+ MemoryLocal local;
+
+ EXPECT_EQ(1, local.ReadTag(mapping));
+ EXPECT_EQ(0, local.ReadTag(mapping + 16));
+}
+
+} // namespace unwindstack
+
+#endif
diff --git a/libunwindstack/tests/MemoryRemoteTest.cpp b/libunwindstack/tests/MemoryRemoteTest.cpp
index c90dedc..621893b 100644
--- a/libunwindstack/tests/MemoryRemoteTest.cpp
+++ b/libunwindstack/tests/MemoryRemoteTest.cpp
@@ -26,8 +26,8 @@
#include <vector>
-#include <android-base/test_utils.h>
#include <android-base/file.h>
+#include <android-base/test_utils.h>
#include <gtest/gtest.h>
#include "MemoryRemote.h"
@@ -37,24 +37,7 @@
namespace unwindstack {
-class MemoryRemoteTest : public ::testing::Test {
- protected:
- static bool Attach(pid_t pid) {
- if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
- return false;
- }
-
- return TestQuiescePid(pid);
- }
-
- static bool Detach(pid_t pid) {
- return ptrace(PTRACE_DETACH, pid, 0, 0) == 0;
- }
-
- static constexpr size_t NS_PER_SEC = 1000000000ULL;
-};
-
-TEST_F(MemoryRemoteTest, read) {
+TEST(MemoryRemoteTest, read) {
std::vector<uint8_t> src(1024);
memset(src.data(), 0x4c, 1024);
@@ -66,7 +49,7 @@
ASSERT_LT(0, pid);
TestScopedPidReaper reap(pid);
- ASSERT_TRUE(Attach(pid));
+ ASSERT_TRUE(TestAttach(pid));
MemoryRemote remote(pid);
@@ -76,10 +59,10 @@
ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
}
- ASSERT_TRUE(Detach(pid));
+ ASSERT_TRUE(TestDetach(pid));
}
-TEST_F(MemoryRemoteTest, read_large) {
+TEST(MemoryRemoteTest, read_large) {
static constexpr size_t kTotalPages = 245;
std::vector<uint8_t> src(kTotalPages * getpagesize());
for (size_t i = 0; i < kTotalPages; i++) {
@@ -95,7 +78,7 @@
ASSERT_LT(0, pid);
TestScopedPidReaper reap(pid);
- ASSERT_TRUE(Attach(pid));
+ ASSERT_TRUE(TestAttach(pid));
MemoryRemote remote(pid);
@@ -105,10 +88,10 @@
ASSERT_EQ(i / getpagesize(), dst[i]) << "Failed at byte " << i;
}
- ASSERT_TRUE(Detach(pid));
+ ASSERT_TRUE(TestDetach(pid));
}
-TEST_F(MemoryRemoteTest, read_partial) {
+TEST(MemoryRemoteTest, read_partial) {
char* mapping = static_cast<char*>(
mmap(nullptr, 4 * getpagesize(), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
ASSERT_NE(MAP_FAILED, mapping);
@@ -128,7 +111,7 @@
// Unmap from our process.
ASSERT_EQ(0, munmap(mapping, 3 * getpagesize()));
- ASSERT_TRUE(Attach(pid));
+ ASSERT_TRUE(TestAttach(pid));
MemoryRemote remote(pid);
@@ -149,10 +132,10 @@
ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
}
- ASSERT_TRUE(Detach(pid));
+ ASSERT_TRUE(TestDetach(pid));
}
-TEST_F(MemoryRemoteTest, read_fail) {
+TEST(MemoryRemoteTest, read_fail) {
int pagesize = getpagesize();
void* src = mmap(nullptr, pagesize * 2, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE,-1, 0);
memset(src, 0x4c, pagesize * 2);
@@ -169,7 +152,7 @@
ASSERT_LT(0, pid);
TestScopedPidReaper reap(pid);
- ASSERT_TRUE(Attach(pid));
+ ASSERT_TRUE(TestAttach(pid));
MemoryRemote remote(pid);
@@ -188,10 +171,10 @@
ASSERT_EQ(0, munmap(src, pagesize));
- ASSERT_TRUE(Detach(pid));
+ ASSERT_TRUE(TestDetach(pid));
}
-TEST_F(MemoryRemoteTest, read_overflow) {
+TEST(MemoryRemoteTest, read_overflow) {
pid_t pid;
if ((pid = fork()) == 0) {
while (true)
@@ -201,7 +184,7 @@
ASSERT_LT(0, pid);
TestScopedPidReaper reap(pid);
- ASSERT_TRUE(Attach(pid));
+ ASSERT_TRUE(TestAttach(pid));
MemoryRemote remote(pid);
@@ -209,10 +192,10 @@
std::vector<uint8_t> dst(200);
ASSERT_FALSE(remote.ReadFully(UINT64_MAX - 100, dst.data(), 200));
- ASSERT_TRUE(Detach(pid));
+ ASSERT_TRUE(TestDetach(pid));
}
-TEST_F(MemoryRemoteTest, read_illegal) {
+TEST(MemoryRemoteTest, read_illegal) {
pid_t pid;
if ((pid = fork()) == 0) {
while (true);
@@ -221,7 +204,7 @@
ASSERT_LT(0, pid);
TestScopedPidReaper reap(pid);
- ASSERT_TRUE(Attach(pid));
+ ASSERT_TRUE(TestAttach(pid));
MemoryRemote remote(pid);
@@ -229,10 +212,10 @@
ASSERT_FALSE(remote.ReadFully(0, dst.data(), 1));
ASSERT_FALSE(remote.ReadFully(0, dst.data(), 100));
- ASSERT_TRUE(Detach(pid));
+ ASSERT_TRUE(TestDetach(pid));
}
-TEST_F(MemoryRemoteTest, read_mprotect_hole) {
+TEST(MemoryRemoteTest, read_mprotect_hole) {
size_t page_size = getpagesize();
void* mapping =
mmap(nullptr, 3 * getpagesize(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
@@ -250,7 +233,7 @@
ASSERT_EQ(0, munmap(mapping, 3 * page_size));
- ASSERT_TRUE(Attach(pid));
+ ASSERT_TRUE(TestAttach(pid));
MemoryRemote remote(pid);
std::vector<uint8_t> dst(getpagesize() * 4, 0xCC);
@@ -263,9 +246,11 @@
for (size_t i = read_size; i < dst.size(); ++i) {
ASSERT_EQ(0xCC, dst[i]);
}
+
+ ASSERT_TRUE(TestDetach(pid));
}
-TEST_F(MemoryRemoteTest, read_munmap_hole) {
+TEST(MemoryRemoteTest, read_munmap_hole) {
size_t page_size = getpagesize();
void* mapping =
mmap(nullptr, 3 * getpagesize(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
@@ -285,7 +270,7 @@
ASSERT_EQ(0, munmap(mapping, page_size));
ASSERT_EQ(0, munmap(static_cast<char*>(mapping) + 2 * page_size, page_size));
- ASSERT_TRUE(Attach(pid));
+ ASSERT_TRUE(TestAttach(pid));
MemoryRemote remote(pid);
std::vector<uint8_t> dst(getpagesize() * 4, 0xCC);
@@ -297,11 +282,13 @@
for (size_t i = read_size; i < dst.size(); ++i) {
ASSERT_EQ(0xCC, dst[i]);
}
+
+ ASSERT_TRUE(TestDetach(pid));
}
// Verify that the memory remote object chooses a memory read function
// properly. Either process_vm_readv or ptrace.
-TEST_F(MemoryRemoteTest, read_choose_correctly) {
+TEST(MemoryRemoteTest, read_choose_correctly) {
size_t page_size = getpagesize();
void* mapping =
mmap(nullptr, 2 * getpagesize(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
@@ -320,7 +307,7 @@
ASSERT_EQ(0, munmap(mapping, 2 * page_size));
- ASSERT_TRUE(Attach(pid));
+ ASSERT_TRUE(TestAttach(pid));
// We know that process_vm_readv of a mprotect'd PROT_NONE region will fail.
// Read from the PROT_NONE area first to force the choice of ptrace.
@@ -348,6 +335,8 @@
bytes = remote_readv.Read(reinterpret_cast<uint64_t>(mapping) + page_size, &value, sizeof(value));
ASSERT_EQ(sizeof(value), bytes);
ASSERT_EQ(0xfcfcfcfcU, value);
+
+ ASSERT_TRUE(TestDetach(pid));
}
} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryTest.cpp b/libunwindstack/tests/MemoryTest.cpp
index 3655984..8a8eb24 100644
--- a/libunwindstack/tests/MemoryTest.cpp
+++ b/libunwindstack/tests/MemoryTest.cpp
@@ -59,10 +59,10 @@
memory.SetMemory(100, name.c_str(), name.size() + 1);
std::string dst_name;
- ASSERT_TRUE(memory.ReadString(100, &dst_name));
+ ASSERT_TRUE(memory.ReadString(100, &dst_name, 100));
ASSERT_EQ("string_in_memory", dst_name);
- ASSERT_TRUE(memory.ReadString(107, &dst_name));
+ ASSERT_TRUE(memory.ReadString(107, &dst_name, 100));
ASSERT_EQ("in_memory", dst_name);
// Set size greater than string.
@@ -82,15 +82,56 @@
std::string dst_name;
// Read from a non-existant address.
- ASSERT_FALSE(memory.ReadString(100, &dst_name));
+ ASSERT_FALSE(memory.ReadString(100, &dst_name, 100));
// This should fail because there is no terminating '\0'.
- ASSERT_FALSE(memory.ReadString(0, &dst_name));
+ ASSERT_FALSE(memory.ReadString(0, &dst_name, 100));
// This should pass because there is a terminating '\0'.
memory.SetData8(name.size(), '\0');
- ASSERT_TRUE(memory.ReadString(0, &dst_name));
+ ASSERT_TRUE(memory.ReadString(0, &dst_name, 100));
ASSERT_EQ("short", dst_name);
}
+TEST(MemoryTest, read_string_long) {
+ // This string should be greater than 768 characters long (greater than 3 times
+ // the buffer in the ReadString function) to read multiple blocks.
+ static constexpr char kLongString[] =
+ "one two three four five six seven eight nine ten eleven twelve thirteen fourteen fifteen "
+ "sixteen seventeen eightteen nineteen twenty twenty-one twenty-two twenty-three twenty-four "
+ "twenty-five twenty-six twenty-seven twenty-eight twenty-nine thirty thirty-one thirty-two "
+ "thirty-three thirty-four thirty-five thirty-six thirty-seven thirty-eight thirty-nine forty "
+ "forty-one forty-two forty-three forty-four forty-five forty-size forty-seven forty-eight "
+ "forty-nine fifty fifty-one fifty-two fifty-three fifty-four fifty-five fifty-six "
+ "fifty-seven fifty-eight fifty-nine sixty sixty-one sixty-two sixty-three sixty-four "
+ "sixty-five sixty-six sixty-seven sixty-eight sixty-nine seventy seventy-one seventy-two "
+ "seventy-three seventy-four seventy-five seventy-six seventy-seven seventy-eight "
+ "seventy-nine eighty";
+
+ MemoryFake memory;
+
+ memory.SetMemory(100, kLongString, sizeof(kLongString));
+
+ std::string dst_name;
+ ASSERT_TRUE(memory.ReadString(100, &dst_name, sizeof(kLongString)));
+ ASSERT_EQ(kLongString, dst_name);
+
+ std::string expected_str(kLongString, 255);
+ memory.SetMemory(100, expected_str.data(), expected_str.length() + 1);
+ ASSERT_TRUE(memory.ReadString(100, &dst_name, 256));
+ ASSERT_EQ(expected_str, dst_name);
+ ASSERT_FALSE(memory.ReadString(100, &dst_name, 255));
+
+ expected_str = std::string(kLongString, 256);
+ memory.SetMemory(100, expected_str.data(), expected_str.length() + 1);
+ ASSERT_TRUE(memory.ReadString(100, &dst_name, 257));
+ ASSERT_EQ(expected_str, dst_name);
+ ASSERT_FALSE(memory.ReadString(100, &dst_name, 256));
+
+ expected_str = std::string(kLongString, 299);
+ memory.SetMemory(100, expected_str.data(), expected_str.length() + 1);
+ ASSERT_TRUE(memory.ReadString(100, &dst_name, 300));
+ ASSERT_EQ(expected_str, dst_name);
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/RegsFake.h b/libunwindstack/tests/RegsFake.h
index 207d46e..75fc9d0 100644
--- a/libunwindstack/tests/RegsFake.h
+++ b/libunwindstack/tests/RegsFake.h
@@ -54,8 +54,6 @@
return fake_arch_ == ARCH_ARM || fake_arch_ == ARCH_X86 || fake_arch_ == ARCH_MIPS;
}
- uint64_t GetPcAdjustment(uint64_t, Elf*) override { return 2; }
-
bool StepIfSignalHandler(uint64_t, Elf*, Memory*) override { return false; }
void FakeSetArch(ArchEnum arch) { fake_arch_ = arch; }
@@ -86,7 +84,6 @@
void set_pc(uint64_t pc) override { fake_pc_ = pc; }
void set_sp(uint64_t sp) override { fake_sp_ = sp; }
- uint64_t GetPcAdjustment(uint64_t, Elf*) override { return 0; }
bool SetPcFromReturnAddress(Memory*) override { return false; }
bool StepIfSignalHandler(uint64_t, Elf*, Memory*) override { return false; }
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
index 0a33e2f..e4fc6f0 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -93,123 +93,104 @@
}
TEST_F(RegsTest, rel_pc) {
- RegsArm64 arm64;
- EXPECT_EQ(4U, arm64.GetPcAdjustment(0x10, elf_.get()));
- EXPECT_EQ(4U, arm64.GetPcAdjustment(0x4, elf_.get()));
- EXPECT_EQ(0U, arm64.GetPcAdjustment(0x3, elf_.get()));
- EXPECT_EQ(0U, arm64.GetPcAdjustment(0x2, elf_.get()));
- EXPECT_EQ(0U, arm64.GetPcAdjustment(0x1, elf_.get()));
- EXPECT_EQ(0U, arm64.GetPcAdjustment(0x0, elf_.get()));
+ EXPECT_EQ(4U, GetPcAdjustment(0x10, elf_.get(), ARCH_ARM64));
+ EXPECT_EQ(4U, GetPcAdjustment(0x4, elf_.get(), ARCH_ARM64));
+ EXPECT_EQ(0U, GetPcAdjustment(0x3, elf_.get(), ARCH_ARM64));
+ EXPECT_EQ(0U, GetPcAdjustment(0x2, elf_.get(), ARCH_ARM64));
+ EXPECT_EQ(0U, GetPcAdjustment(0x1, elf_.get(), ARCH_ARM64));
+ EXPECT_EQ(0U, GetPcAdjustment(0x0, elf_.get(), ARCH_ARM64));
- RegsX86 x86;
- EXPECT_EQ(1U, x86.GetPcAdjustment(0x100, elf_.get()));
- EXPECT_EQ(1U, x86.GetPcAdjustment(0x2, elf_.get()));
- EXPECT_EQ(1U, x86.GetPcAdjustment(0x1, elf_.get()));
- EXPECT_EQ(0U, x86.GetPcAdjustment(0x0, elf_.get()));
+ EXPECT_EQ(1U, GetPcAdjustment(0x100, elf_.get(), ARCH_X86));
+ EXPECT_EQ(1U, GetPcAdjustment(0x2, elf_.get(), ARCH_X86));
+ EXPECT_EQ(1U, GetPcAdjustment(0x1, elf_.get(), ARCH_X86));
+ EXPECT_EQ(0U, GetPcAdjustment(0x0, elf_.get(), ARCH_X86));
- RegsX86_64 x86_64;
- EXPECT_EQ(1U, x86_64.GetPcAdjustment(0x100, elf_.get()));
- EXPECT_EQ(1U, x86_64.GetPcAdjustment(0x2, elf_.get()));
- EXPECT_EQ(1U, x86_64.GetPcAdjustment(0x1, elf_.get()));
- EXPECT_EQ(0U, x86_64.GetPcAdjustment(0x0, elf_.get()));
+ EXPECT_EQ(1U, GetPcAdjustment(0x100, elf_.get(), ARCH_X86_64));
+ EXPECT_EQ(1U, GetPcAdjustment(0x2, elf_.get(), ARCH_X86_64));
+ EXPECT_EQ(1U, GetPcAdjustment(0x1, elf_.get(), ARCH_X86_64));
+ EXPECT_EQ(0U, GetPcAdjustment(0x0, elf_.get(), ARCH_X86_64));
- RegsMips mips;
- EXPECT_EQ(8U, mips.GetPcAdjustment(0x10, elf_.get()));
- EXPECT_EQ(8U, mips.GetPcAdjustment(0x8, elf_.get()));
- EXPECT_EQ(0U, mips.GetPcAdjustment(0x7, elf_.get()));
- EXPECT_EQ(0U, mips.GetPcAdjustment(0x6, elf_.get()));
- EXPECT_EQ(0U, mips.GetPcAdjustment(0x5, elf_.get()));
- EXPECT_EQ(0U, mips.GetPcAdjustment(0x4, elf_.get()));
- EXPECT_EQ(0U, mips.GetPcAdjustment(0x3, elf_.get()));
- EXPECT_EQ(0U, mips.GetPcAdjustment(0x2, elf_.get()));
- EXPECT_EQ(0U, mips.GetPcAdjustment(0x1, elf_.get()));
- EXPECT_EQ(0U, mips.GetPcAdjustment(0x0, elf_.get()));
+ EXPECT_EQ(8U, GetPcAdjustment(0x10, elf_.get(), ARCH_MIPS));
+ EXPECT_EQ(8U, GetPcAdjustment(0x8, elf_.get(), ARCH_MIPS));
+ EXPECT_EQ(0U, GetPcAdjustment(0x7, elf_.get(), ARCH_MIPS));
+ EXPECT_EQ(0U, GetPcAdjustment(0x6, elf_.get(), ARCH_MIPS));
+ EXPECT_EQ(0U, GetPcAdjustment(0x5, elf_.get(), ARCH_MIPS));
+ EXPECT_EQ(0U, GetPcAdjustment(0x4, elf_.get(), ARCH_MIPS));
+ EXPECT_EQ(0U, GetPcAdjustment(0x3, elf_.get(), ARCH_MIPS));
+ EXPECT_EQ(0U, GetPcAdjustment(0x2, elf_.get(), ARCH_MIPS));
+ EXPECT_EQ(0U, GetPcAdjustment(0x1, elf_.get(), ARCH_MIPS));
+ EXPECT_EQ(0U, GetPcAdjustment(0x0, elf_.get(), ARCH_MIPS));
- RegsMips64 mips64;
- EXPECT_EQ(8U, mips64.GetPcAdjustment(0x10, elf_.get()));
- EXPECT_EQ(8U, mips64.GetPcAdjustment(0x8, elf_.get()));
- EXPECT_EQ(0U, mips64.GetPcAdjustment(0x7, elf_.get()));
- EXPECT_EQ(0U, mips64.GetPcAdjustment(0x6, elf_.get()));
- EXPECT_EQ(0U, mips64.GetPcAdjustment(0x5, elf_.get()));
- EXPECT_EQ(0U, mips64.GetPcAdjustment(0x4, elf_.get()));
- EXPECT_EQ(0U, mips64.GetPcAdjustment(0x3, elf_.get()));
- EXPECT_EQ(0U, mips64.GetPcAdjustment(0x2, elf_.get()));
- EXPECT_EQ(0U, mips64.GetPcAdjustment(0x1, elf_.get()));
- EXPECT_EQ(0U, mips64.GetPcAdjustment(0x0, elf_.get()));
+ EXPECT_EQ(8U, GetPcAdjustment(0x10, elf_.get(), ARCH_MIPS64));
+ EXPECT_EQ(8U, GetPcAdjustment(0x8, elf_.get(), ARCH_MIPS64));
+ EXPECT_EQ(0U, GetPcAdjustment(0x7, elf_.get(), ARCH_MIPS64));
+ EXPECT_EQ(0U, GetPcAdjustment(0x6, elf_.get(), ARCH_MIPS64));
+ EXPECT_EQ(0U, GetPcAdjustment(0x5, elf_.get(), ARCH_MIPS64));
+ EXPECT_EQ(0U, GetPcAdjustment(0x4, elf_.get(), ARCH_MIPS64));
+ EXPECT_EQ(0U, GetPcAdjustment(0x3, elf_.get(), ARCH_MIPS64));
+ EXPECT_EQ(0U, GetPcAdjustment(0x2, elf_.get(), ARCH_MIPS64));
+ EXPECT_EQ(0U, GetPcAdjustment(0x1, elf_.get(), ARCH_MIPS64));
+ EXPECT_EQ(0U, GetPcAdjustment(0x0, elf_.get(), ARCH_MIPS64));
}
TEST_F(RegsTest, rel_pc_arm) {
- RegsArm arm;
-
// Check fence posts.
elf_->FakeSetLoadBias(0);
- EXPECT_EQ(2U, arm.GetPcAdjustment(0x5, elf_.get()));
- EXPECT_EQ(2U, arm.GetPcAdjustment(0x4, elf_.get()));
- EXPECT_EQ(2U, arm.GetPcAdjustment(0x3, elf_.get()));
- EXPECT_EQ(2U, arm.GetPcAdjustment(0x2, elf_.get()));
- EXPECT_EQ(0U, arm.GetPcAdjustment(0x1, elf_.get()));
- EXPECT_EQ(0U, arm.GetPcAdjustment(0x0, elf_.get()));
+ EXPECT_EQ(2U, GetPcAdjustment(0x5, elf_.get(), ARCH_ARM));
+ EXPECT_EQ(2U, GetPcAdjustment(0x4, elf_.get(), ARCH_ARM));
+ EXPECT_EQ(2U, GetPcAdjustment(0x3, elf_.get(), ARCH_ARM));
+ EXPECT_EQ(2U, GetPcAdjustment(0x2, elf_.get(), ARCH_ARM));
+ EXPECT_EQ(0U, GetPcAdjustment(0x1, elf_.get(), ARCH_ARM));
+ EXPECT_EQ(0U, GetPcAdjustment(0x0, elf_.get(), ARCH_ARM));
elf_->FakeSetLoadBias(0x100);
- EXPECT_EQ(0U, arm.GetPcAdjustment(0x1, elf_.get()));
- EXPECT_EQ(2U, arm.GetPcAdjustment(0x2, elf_.get()));
- EXPECT_EQ(2U, arm.GetPcAdjustment(0xff, elf_.get()));
- EXPECT_EQ(2U, arm.GetPcAdjustment(0x105, elf_.get()));
- EXPECT_EQ(2U, arm.GetPcAdjustment(0x104, elf_.get()));
- EXPECT_EQ(2U, arm.GetPcAdjustment(0x103, elf_.get()));
- EXPECT_EQ(2U, arm.GetPcAdjustment(0x102, elf_.get()));
- EXPECT_EQ(0U, arm.GetPcAdjustment(0x101, elf_.get()));
- EXPECT_EQ(0U, arm.GetPcAdjustment(0x100, elf_.get()));
+ EXPECT_EQ(0U, GetPcAdjustment(0x1, elf_.get(), ARCH_ARM));
+ EXPECT_EQ(2U, GetPcAdjustment(0x2, elf_.get(), ARCH_ARM));
+ EXPECT_EQ(2U, GetPcAdjustment(0xff, elf_.get(), ARCH_ARM));
+ EXPECT_EQ(2U, GetPcAdjustment(0x105, elf_.get(), ARCH_ARM));
+ EXPECT_EQ(2U, GetPcAdjustment(0x104, elf_.get(), ARCH_ARM));
+ EXPECT_EQ(2U, GetPcAdjustment(0x103, elf_.get(), ARCH_ARM));
+ EXPECT_EQ(2U, GetPcAdjustment(0x102, elf_.get(), ARCH_ARM));
+ EXPECT_EQ(0U, GetPcAdjustment(0x101, elf_.get(), ARCH_ARM));
+ EXPECT_EQ(0U, GetPcAdjustment(0x100, elf_.get(), ARCH_ARM));
// Check thumb instructions handling.
elf_->FakeSetLoadBias(0);
memory_->SetData32(0x2000, 0);
- EXPECT_EQ(2U, arm.GetPcAdjustment(0x2005, elf_.get()));
+ EXPECT_EQ(2U, GetPcAdjustment(0x2005, elf_.get(), ARCH_ARM));
memory_->SetData32(0x2000, 0xe000f000);
- EXPECT_EQ(4U, arm.GetPcAdjustment(0x2005, elf_.get()));
+ EXPECT_EQ(4U, GetPcAdjustment(0x2005, elf_.get(), ARCH_ARM));
elf_->FakeSetLoadBias(0x400);
memory_->SetData32(0x2100, 0);
- EXPECT_EQ(2U, arm.GetPcAdjustment(0x2505, elf_.get()));
+ EXPECT_EQ(2U, GetPcAdjustment(0x2505, elf_.get(), ARCH_ARM));
memory_->SetData32(0x2100, 0xf111f111);
- EXPECT_EQ(4U, arm.GetPcAdjustment(0x2505, elf_.get()));
+ EXPECT_EQ(4U, GetPcAdjustment(0x2505, elf_.get(), ARCH_ARM));
}
TEST_F(RegsTest, elf_invalid) {
- RegsArm regs_arm;
- RegsArm64 regs_arm64;
- RegsX86 regs_x86;
- RegsX86_64 regs_x86_64;
- RegsMips regs_mips;
- RegsMips64 regs_mips64;
MapInfo map_info(nullptr, nullptr, 0x1000, 0x2000, 0, 0, "");
Elf* invalid_elf = new Elf(nullptr);
map_info.elf.reset(invalid_elf);
- regs_arm.set_pc(0x1500);
- EXPECT_EQ(0x500U, invalid_elf->GetRelPc(regs_arm.pc(), &map_info));
- EXPECT_EQ(2U, regs_arm.GetPcAdjustment(0x500U, invalid_elf));
- EXPECT_EQ(2U, regs_arm.GetPcAdjustment(0x511U, invalid_elf));
+ EXPECT_EQ(0x500U, invalid_elf->GetRelPc(0x1500, &map_info));
+ EXPECT_EQ(2U, GetPcAdjustment(0x500U, invalid_elf, ARCH_ARM));
+ EXPECT_EQ(2U, GetPcAdjustment(0x511U, invalid_elf, ARCH_ARM));
- regs_arm64.set_pc(0x1600);
- EXPECT_EQ(0x600U, invalid_elf->GetRelPc(regs_arm64.pc(), &map_info));
- EXPECT_EQ(4U, regs_arm64.GetPcAdjustment(0x600U, invalid_elf));
+ EXPECT_EQ(0x600U, invalid_elf->GetRelPc(0x1600, &map_info));
+ EXPECT_EQ(4U, GetPcAdjustment(0x600U, invalid_elf, ARCH_ARM64));
- regs_x86.set_pc(0x1700);
- EXPECT_EQ(0x700U, invalid_elf->GetRelPc(regs_x86.pc(), &map_info));
- EXPECT_EQ(1U, regs_x86.GetPcAdjustment(0x700U, invalid_elf));
+ EXPECT_EQ(0x700U, invalid_elf->GetRelPc(0x1700, &map_info));
+ EXPECT_EQ(1U, GetPcAdjustment(0x700U, invalid_elf, ARCH_X86));
- regs_x86_64.set_pc(0x1800);
- EXPECT_EQ(0x800U, invalid_elf->GetRelPc(regs_x86_64.pc(), &map_info));
- EXPECT_EQ(1U, regs_x86_64.GetPcAdjustment(0x800U, invalid_elf));
+ EXPECT_EQ(0x800U, invalid_elf->GetRelPc(0x1800, &map_info));
+ EXPECT_EQ(1U, GetPcAdjustment(0x800U, invalid_elf, ARCH_X86_64));
- regs_mips.set_pc(0x1900);
- EXPECT_EQ(0x900U, invalid_elf->GetRelPc(regs_mips.pc(), &map_info));
- EXPECT_EQ(8U, regs_mips.GetPcAdjustment(0x900U, invalid_elf));
+ EXPECT_EQ(0x900U, invalid_elf->GetRelPc(0x1900, &map_info));
+ EXPECT_EQ(8U, GetPcAdjustment(0x900U, invalid_elf, ARCH_MIPS));
- regs_mips64.set_pc(0x1a00);
- EXPECT_EQ(0xa00U, invalid_elf->GetRelPc(regs_mips64.pc(), &map_info));
- EXPECT_EQ(8U, regs_mips64.GetPcAdjustment(0xa00U, invalid_elf));
+ EXPECT_EQ(0xa00U, invalid_elf->GetRelPc(0x1a00, &map_info));
+ EXPECT_EQ(8U, GetPcAdjustment(0xa00U, invalid_elf, ARCH_MIPS64));
}
TEST_F(RegsTest, arm_verify_sp_pc) {
diff --git a/libunwindstack/tests/SymbolsTest.cpp b/libunwindstack/tests/SymbolsTest.cpp
index c58aeff..9afbeec 100644
--- a/libunwindstack/tests/SymbolsTest.cpp
+++ b/libunwindstack/tests/SymbolsTest.cpp
@@ -185,18 +185,21 @@
std::string fake_name;
this->InitSym(&sym, 0x5000, 0x10, 0x40);
+ this->memory_.SetMemoryBlock(offset, entry_size, 0);
this->memory_.SetMemory(offset, &sym, sizeof(sym));
fake_name = "function_one";
this->memory_.SetMemory(0x2040, fake_name.c_str(), fake_name.size() + 1);
offset += entry_size;
this->InitSym(&sym, 0x3004, 0x200, 0x100);
+ this->memory_.SetMemoryBlock(offset, entry_size, 0);
this->memory_.SetMemory(offset, &sym, sizeof(sym));
fake_name = "function_two";
this->memory_.SetMemory(0x2100, fake_name.c_str(), fake_name.size() + 1);
offset += entry_size;
this->InitSym(&sym, 0xa010, 0x20, 0x230);
+ this->memory_.SetMemoryBlock(offset, entry_size, 0);
this->memory_.SetMemory(offset, &sym, sizeof(sym));
fake_name = "function_three";
this->memory_.SetMemory(0x2230, fake_name.c_str(), fake_name.size() + 1);
@@ -274,7 +277,9 @@
// Do call that should cache all of the entries (except the string data).
std::string name;
uint64_t func_offset;
- ASSERT_FALSE(symbols.GetName<TypeParam>(0x6000, &this->memory_, &name, &func_offset));
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x5000, &this->memory_, &name, &func_offset));
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x2000, &this->memory_, &name, &func_offset));
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x1000, &this->memory_, &name, &func_offset));
this->memory_.Clear();
ASSERT_FALSE(symbols.GetName<TypeParam>(0x6000, &this->memory_, &name, &func_offset));
diff --git a/libunwindstack/tests/TestUtils.h b/libunwindstack/tests/TestUtils.h
index a4d7b9b..0685006 100644
--- a/libunwindstack/tests/TestUtils.h
+++ b/libunwindstack/tests/TestUtils.h
@@ -21,6 +21,7 @@
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
+#include <unistd.h>
namespace unwindstack {
@@ -50,6 +51,18 @@
return ready;
}
+inline bool TestAttach(pid_t pid) {
+ if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
+ return false;
+ }
+
+ return TestQuiescePid(pid);
+}
+
+inline bool TestDetach(pid_t pid) {
+ return ptrace(PTRACE_DETACH, pid, 0, 0) == 0;
+}
+
void TestCheckForLeaks(void (*unwind_func)(void*), void* data);
} // namespace unwindstack
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index ef1950c..dd33aa9 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -161,8 +161,8 @@
regs_.set_pc(0x1000);
regs_.set_sp(0x10000);
- ElfInterfaceFake::FakePushStepData(StepData(0x1102, 0x10010, false));
- ElfInterfaceFake::FakePushStepData(StepData(0x1202, 0x10020, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x1104, 0x10010, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x1204, 0x10020, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
@@ -225,8 +225,8 @@
regs_.set_pc(0x1000);
regs_.set_sp(0x10000);
- ElfInterfaceFake::FakePushStepData(StepData(0x1102, 0x10010, false));
- ElfInterfaceFake::FakePushStepData(StepData(0x1202, 0x10020, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x1104, 0x10010, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x1204, 0x10020, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
@@ -445,7 +445,7 @@
TEST_F(UnwinderTest, max_frames) {
for (size_t i = 0; i < 30; i++) {
ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame" + std::to_string(i), i));
- ElfInterfaceFake::FakePushStepData(StepData(0x1102 + i * 0x100, 0x10010 + i * 0x10, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x1104 + i * 0x100, 0x10010 + i * 0x10, false));
}
regs_.set_pc(0x1000);
@@ -484,12 +484,12 @@
regs_.set_pc(0x20000);
regs_.set_sp(0x10000);
- ElfInterfaceFake::FakePushStepData(StepData(0x23002, 0x10010, false));
- ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false));
- ElfInterfaceFake::FakePushStepData(StepData(0x20002, 0x10030, false));
- ElfInterfaceFake::FakePushStepData(StepData(0x21002, 0x10040, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x23004, 0x10010, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x23104, 0x10020, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x20004, 0x10030, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x21004, 0x10040, false));
ElfInterfaceFake::FakePushStepData(StepData(0x1002, 0x10050, false));
- ElfInterfaceFake::FakePushStepData(StepData(0x21002, 0x10060, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x21004, 0x10060, false));
ElfInterfaceFake::FakePushStepData(StepData(0x23002, 0x10070, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
@@ -553,7 +553,7 @@
regs_.set_pc(0x1000);
regs_.set_sp(0x63000);
- ElfInterfaceFake::FakePushStepData(StepData(0x21002, 0x50020, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x21004, 0x50020, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
@@ -670,10 +670,10 @@
// Fake as if code called a nullptr function.
regs_.set_pc(0);
regs_.set_sp(0x10000);
- regs_.FakeSetReturnAddress(0x1202);
+ regs_.FakeSetReturnAddress(0x1204);
regs_.FakeSetReturnAddressValid(true);
- ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x23104, 0x10020, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
@@ -789,7 +789,7 @@
// Fake as if code called a nullptr function.
regs_.set_pc(0);
regs_.set_sp(0x10000);
- regs_.FakeSetReturnAddress(0x1202);
+ regs_.FakeSetReturnAddress(0x1204);
regs_.FakeSetReturnAddressValid(true);
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
@@ -858,8 +858,8 @@
// Fake as if code called a nullptr function.
regs_.set_pc(0x1000);
regs_.set_sp(0x10000);
- ElfInterfaceFake::FakePushStepData(StepData(0x43402, 0x10010, false));
- ElfInterfaceFake::FakePushStepData(StepData(0x53502, 0x10020, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x43404, 0x10010, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x53504, 0x10020, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
@@ -915,11 +915,11 @@
regs_.set_pc(0x1000);
regs_.set_sp(0x10000);
- ElfInterfaceFake::FakePushStepData(StepData(0x33402, 0x10010, false));
- ElfInterfaceFake::FakePushStepData(StepData(0x33502, 0x10020, false));
- ElfInterfaceFake::FakePushStepData(StepData(0x33502, 0x10020, false));
- ElfInterfaceFake::FakePushStepData(StepData(0x33502, 0x10020, false));
- ElfInterfaceFake::FakePushStepData(StepData(0x33502, 0x10020, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x33404, 0x10010, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x33504, 0x10020, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x33504, 0x10020, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x33504, 0x10020, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x33504, 0x10020, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
@@ -1113,7 +1113,7 @@
regs_.set_pc(0x1000);
regs_.set_sp(0x10000);
regs_.FakeSetDexPc(0xa3400);
- ElfInterfaceFake::FakePushStepData(StepData(0x33402, 0x10010, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x33404, 0x10010, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 9ddbedf..ea39d34 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -18,6 +18,11 @@
recovery_available: true,
host_supported: true,
native_bridge_supported: true,
+ apex_available: [
+ "//apex_available:platform",
+ "//apex_available:anyapex",
+ ],
+ min_sdk_version: "apex_inherit",
header_libs: [
"liblog_headers",
@@ -158,6 +163,7 @@
"//apex_available:anyapex",
"//apex_available:platform",
],
+ min_sdk_version: "apex_inherit",
}
cc_library {
@@ -169,8 +175,8 @@
],
shared_libs: [
- "libutils",
- "libbacktrace",
+ "libutils",
+ "libbacktrace",
],
target: {
@@ -188,6 +194,45 @@
},
}
+cc_defaults {
+ name: "libutils_fuzz_defaults",
+ host_supported: true,
+ shared_libs: [
+ "libutils",
+ "libbase",
+ ],
+}
+
+cc_fuzz {
+ name: "libutils_fuzz_bitset",
+ defaults: ["libutils_fuzz_defaults"],
+ srcs: ["BitSet_fuzz.cpp"],
+}
+
+cc_fuzz {
+ name: "libutils_fuzz_filemap",
+ defaults: ["libutils_fuzz_defaults"],
+ srcs: ["FileMap_fuzz.cpp"],
+}
+
+cc_fuzz {
+ name: "libutils_fuzz_string8",
+ defaults: ["libutils_fuzz_defaults"],
+ srcs: ["String8_fuzz.cpp"],
+}
+
+cc_fuzz {
+ name: "libutils_fuzz_string16",
+ defaults: ["libutils_fuzz_defaults"],
+ srcs: ["String16_fuzz.cpp"],
+}
+
+cc_fuzz {
+ name: "libutils_fuzz_vector",
+ defaults: ["libutils_fuzz_defaults"],
+ srcs: ["Vector_fuzz.cpp"],
+}
+
cc_test {
name: "libutils_test",
host_supported: true,
@@ -201,6 +246,7 @@
"String8_test.cpp",
"String16_test.cpp",
"StrongPointer_test.cpp",
+ "Timers_test.cpp",
"Unicode_test.cpp",
"Vector_test.cpp",
],
diff --git a/libutils/BitSet_fuzz.cpp b/libutils/BitSet_fuzz.cpp
new file mode 100644
index 0000000..2e6043c
--- /dev/null
+++ b/libutils/BitSet_fuzz.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright 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 <functional>
+
+#include "fuzzer/FuzzedDataProvider.h"
+#include "utils/BitSet.h"
+static constexpr uint8_t MAX_OPERATIONS = 50;
+
+// We need to handle both 32 and 64 bit bitsets, so we use a function template
+// here. Sadly, std::function can't be generic, so we generate a vector of
+// std::functions using this function.
+template <typename T>
+std::vector<std::function<void(T, uint32_t)>> getOperationsForType() {
+ return {
+ [](T bs, uint32_t val) -> void { bs.markBit(val); },
+ [](T bs, uint32_t val) -> void { bs.valueForBit(val); },
+ [](T bs, uint32_t val) -> void { bs.hasBit(val); },
+ [](T bs, uint32_t val) -> void { bs.clearBit(val); },
+ [](T bs, uint32_t val) -> void { bs.getIndexOfBit(val); },
+ [](T bs, uint32_t) -> void { bs.clearFirstMarkedBit(); },
+ [](T bs, uint32_t) -> void { bs.markFirstUnmarkedBit(); },
+ [](T bs, uint32_t) -> void { bs.clearLastMarkedBit(); },
+ [](T bs, uint32_t) -> void { bs.clear(); },
+ [](T bs, uint32_t) -> void { bs.count(); },
+ [](T bs, uint32_t) -> void { bs.isEmpty(); },
+ [](T bs, uint32_t) -> void { bs.isFull(); },
+ [](T bs, uint32_t) -> void { bs.firstMarkedBit(); },
+ [](T bs, uint32_t) -> void { bs.lastMarkedBit(); },
+ };
+}
+
+// Our operations for 32 and 64 bit bitsets
+static const std::vector<std::function<void(android::BitSet32, uint32_t)>> thirtyTwoBitOps =
+ getOperationsForType<android::BitSet32>();
+static const std::vector<std::function<void(android::BitSet64, uint32_t)>> sixtyFourBitOps =
+ getOperationsForType<android::BitSet64>();
+
+void runOperationFor32Bit(android::BitSet32 bs, uint32_t bit, uint8_t operation) {
+ thirtyTwoBitOps[operation](bs, bit);
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider dataProvider(data, size);
+ uint32_t thirty_two_base = dataProvider.ConsumeIntegral<uint32_t>();
+ uint64_t sixty_four_base = dataProvider.ConsumeIntegral<uint64_t>();
+ android::BitSet32 b1 = android::BitSet32(thirty_two_base);
+ android::BitSet64 b2 = android::BitSet64(sixty_four_base);
+
+ size_t opsRun = 0;
+ while (dataProvider.remaining_bytes() > 0 && opsRun++ < MAX_OPERATIONS) {
+ uint32_t bit = dataProvider.ConsumeIntegral<uint32_t>();
+ uint8_t op = dataProvider.ConsumeIntegral<uint8_t>();
+ thirtyTwoBitOps[op % thirtyTwoBitOps.size()](b1, bit);
+ sixtyFourBitOps[op % sixtyFourBitOps.size()](b2, bit);
+ }
+ return 0;
+}
diff --git a/libutils/FileMap.cpp b/libutils/FileMap.cpp
index 1202c15..0abb861 100644
--- a/libutils/FileMap.cpp
+++ b/libutils/FileMap.cpp
@@ -189,13 +189,17 @@
int adjust = offset % mPageSize;
off64_t adjOffset = offset - adjust;
- size_t adjLength = length + adjust;
+ size_t adjLength;
+ if (__builtin_add_overflow(length, adjust, &adjLength)) {
+ ALOGE("adjusted length overflow: length %zu adjust %d", length, adjust);
+ return false;
+ }
int flags = MAP_SHARED;
int prot = PROT_READ;
if (!readOnly) prot |= PROT_WRITE;
- void* ptr = mmap(nullptr, adjLength, prot, flags, fd, adjOffset);
+ void* ptr = mmap64(nullptr, adjLength, prot, flags, fd, adjOffset);
if (ptr == MAP_FAILED) {
if (errno == EINVAL && length == 0) {
ptr = nullptr;
diff --git a/libutils/FileMap_fuzz.cpp b/libutils/FileMap_fuzz.cpp
new file mode 100644
index 0000000..d800564
--- /dev/null
+++ b/libutils/FileMap_fuzz.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright 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 <iostream>
+
+#include "android-base/file.h"
+#include "fuzzer/FuzzedDataProvider.h"
+#include "utils/FileMap.h"
+
+static constexpr uint16_t MAX_STR_SIZE = 256;
+static constexpr uint8_t MAX_FILENAME_SIZE = 32;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider dataProvider(data, size);
+ TemporaryFile tf;
+ // Generate file contents
+ std::string contents = dataProvider.ConsumeRandomLengthString(MAX_STR_SIZE);
+ // If we have string contents, dump them into the file.
+ // Otherwise, just leave it as an empty file.
+ if (contents.length() > 0) {
+ const char* bytes = contents.c_str();
+ android::base::WriteStringToFd(bytes, tf.fd);
+ }
+ android::FileMap m;
+ // Generate create() params
+ std::string orig_name = dataProvider.ConsumeRandomLengthString(MAX_FILENAME_SIZE);
+ size_t length = dataProvider.ConsumeIntegralInRange<size_t>(1, SIZE_MAX);
+ off64_t offset = dataProvider.ConsumeIntegralInRange<off64_t>(1, INT64_MAX);
+ bool read_only = dataProvider.ConsumeBool();
+ m.create(orig_name.c_str(), tf.fd, offset, length, read_only);
+ m.getDataOffset();
+ m.getFileName();
+ m.getDataLength();
+ m.getDataPtr();
+ int enum_index = dataProvider.ConsumeIntegral<int>();
+ m.advise(static_cast<android::FileMap::MapAdvice>(enum_index));
+ return 0;
+}
diff --git a/libutils/FileMap_test.cpp b/libutils/FileMap_test.cpp
index 576d89b..fd1c9b0 100644
--- a/libutils/FileMap_test.cpp
+++ b/libutils/FileMap_test.cpp
@@ -32,3 +32,36 @@
ASSERT_EQ(0u, m.getDataLength());
ASSERT_EQ(4096, m.getDataOffset());
}
+
+TEST(FileMap, large_offset) {
+ // Make sure that an offset > INT32_MAX will not fail the create
+ // function. See http://b/155662887.
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+
+ off64_t offset = INT32_MAX + 1024LL;
+
+ // Make the temporary file large enough to pass the mmap.
+ ASSERT_EQ(offset, lseek64(tf.fd, offset, SEEK_SET));
+ char value = 0;
+ ASSERT_EQ(1, write(tf.fd, &value, 1));
+
+ android::FileMap m;
+ ASSERT_TRUE(m.create("test", tf.fd, offset, 0, true));
+ ASSERT_STREQ("test", m.getFileName());
+ ASSERT_EQ(0u, m.getDataLength());
+ ASSERT_EQ(offset, m.getDataOffset());
+}
+
+TEST(FileMap, offset_overflow) {
+ // Make sure that an end that overflows SIZE_MAX will not abort.
+ // See http://b/156997193.
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+
+ off64_t offset = 200;
+ size_t length = SIZE_MAX;
+
+ android::FileMap m;
+ ASSERT_FALSE(m.create("test", tf.fd, offset, length, true));
+}
diff --git a/libutils/String16_fuzz.cpp b/libutils/String16_fuzz.cpp
new file mode 100644
index 0000000..63c2800
--- /dev/null
+++ b/libutils/String16_fuzz.cpp
@@ -0,0 +1,122 @@
+/*
+ * Copyright 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 <iostream>
+
+#include "fuzzer/FuzzedDataProvider.h"
+#include "utils/String16.h"
+static constexpr int MAX_STRING_BYTES = 256;
+static constexpr uint8_t MAX_OPERATIONS = 50;
+
+std::vector<std::function<void(FuzzedDataProvider&, android::String16, android::String16)>>
+ operations = {
+
+ // Bytes and size
+ ([](FuzzedDataProvider&, android::String16 str1, android::String16) -> void {
+ str1.string();
+ }),
+ ([](FuzzedDataProvider&, android::String16 str1, android::String16) -> void {
+ str1.isStaticString();
+ }),
+ ([](FuzzedDataProvider&, android::String16 str1, android::String16) -> void {
+ str1.size();
+ }),
+
+ // Casing
+ ([](FuzzedDataProvider&, android::String16 str1, android::String16) -> void {
+ str1.makeLower();
+ }),
+
+ // Comparison
+ ([](FuzzedDataProvider&, android::String16 str1, android::String16 str2) -> void {
+ str1.startsWith(str2);
+ }),
+ ([](FuzzedDataProvider&, android::String16 str1, android::String16 str2) -> void {
+ str1.contains(str2.string());
+ }),
+ ([](FuzzedDataProvider&, android::String16 str1, android::String16 str2) -> void {
+ str1.compare(str2);
+ }),
+
+ // Append and format
+ ([](FuzzedDataProvider&, android::String16 str1, android::String16 str2) -> void {
+ str1.append(str2);
+ }),
+ ([](FuzzedDataProvider& dataProvider, android::String16 str1,
+ android::String16 str2) -> void {
+ int pos = dataProvider.ConsumeIntegralInRange<int>(0, str1.size());
+ str1.insert(pos, str2.string());
+ }),
+
+ // Find and replace operations
+ ([](FuzzedDataProvider& dataProvider, android::String16 str1,
+ android::String16) -> void {
+ char16_t findChar = dataProvider.ConsumeIntegral<char16_t>();
+ str1.findFirst(findChar);
+ }),
+ ([](FuzzedDataProvider& dataProvider, android::String16 str1,
+ android::String16) -> void {
+ char16_t findChar = dataProvider.ConsumeIntegral<char16_t>();
+ str1.findLast(findChar);
+ }),
+ ([](FuzzedDataProvider& dataProvider, android::String16 str1,
+ android::String16) -> void {
+ char16_t findChar = dataProvider.ConsumeIntegral<char16_t>();
+ char16_t replaceChar = dataProvider.ConsumeIntegral<char16_t>();
+ str1.replaceAll(findChar, replaceChar);
+ }),
+ ([](FuzzedDataProvider& dataProvider, android::String16 str1,
+ android::String16) -> void {
+ size_t len = dataProvider.ConsumeIntegral<size_t>();
+ size_t begin = dataProvider.ConsumeIntegral<size_t>();
+ str1.remove(len, begin);
+ }),
+};
+
+void callFunc(uint8_t index, FuzzedDataProvider& dataProvider, android::String16 str1,
+ android::String16 str2) {
+ operations[index](dataProvider, str1, str2);
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider dataProvider(data, size);
+ // We're generating two char vectors.
+ // First, generate lengths.
+ const size_t kVecOneLen = dataProvider.ConsumeIntegralInRange<size_t>(1, MAX_STRING_BYTES);
+ const size_t kVecTwoLen = dataProvider.ConsumeIntegralInRange<size_t>(1, MAX_STRING_BYTES);
+
+ // Next, populate the vectors
+ std::vector<char> vec = dataProvider.ConsumeBytesWithTerminator<char>(kVecOneLen);
+ std::vector<char> vec_two = dataProvider.ConsumeBytesWithTerminator<char>(kVecTwoLen);
+
+ // Get pointers to their data
+ char* char_one = vec.data();
+ char* char_two = vec_two.data();
+
+ // Create UTF16 representations
+ android::String16 str_one_utf16 = android::String16(char_one);
+ android::String16 str_two_utf16 = android::String16(char_two);
+
+ // Run operations against strings
+ int opsRun = 0;
+ while (dataProvider.remaining_bytes() > 0 && opsRun++ < MAX_OPERATIONS) {
+ uint8_t op = dataProvider.ConsumeIntegralInRange<uint8_t>(0, operations.size() - 1);
+ callFunc(op, dataProvider, str_one_utf16, str_two_utf16);
+ }
+
+ str_one_utf16.remove(0, str_one_utf16.size());
+ str_two_utf16.remove(0, str_two_utf16.size());
+ return 0;
+}
diff --git a/libutils/String8_fuzz.cpp b/libutils/String8_fuzz.cpp
new file mode 100644
index 0000000..2adfe98
--- /dev/null
+++ b/libutils/String8_fuzz.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright 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 <functional>
+#include <iostream>
+
+#include "fuzzer/FuzzedDataProvider.h"
+#include "utils/String8.h"
+
+static constexpr int MAX_STRING_BYTES = 256;
+static constexpr uint8_t MAX_OPERATIONS = 50;
+
+std::vector<std::function<void(FuzzedDataProvider&, android::String8, android::String8)>>
+ operations = {
+
+ // Bytes and size
+ [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
+ str1.bytes();
+ },
+ [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
+ str1.isEmpty();
+ },
+ [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
+ str1.length();
+ },
+ [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
+ str1.size();
+ },
+
+ // Casing
+ [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
+ str1.toUpper();
+ },
+ [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
+ str1.toLower();
+ },
+
+ [](FuzzedDataProvider&, android::String8 str1, android::String8 str2) -> void {
+ str1.removeAll(str2.c_str());
+ },
+ [](FuzzedDataProvider&, android::String8 str1, android::String8 str2) -> void {
+ str1.compare(str2);
+ },
+
+ // Append and format
+ [](FuzzedDataProvider&, android::String8 str1, android::String8 str2) -> void {
+ str1.append(str2);
+ },
+ [](FuzzedDataProvider&, android::String8 str1, android::String8 str2) -> void {
+ str1.appendFormat(str1.c_str(), str2.c_str());
+ },
+ [](FuzzedDataProvider&, android::String8 str1, android::String8 str2) -> void {
+ str1.format(str1.c_str(), str2.c_str());
+ },
+
+ // Find operation
+ [](FuzzedDataProvider& dataProvider, android::String8 str1,
+ android::String8) -> void {
+ // We need to get a value from our fuzzer here.
+ int start_index = dataProvider.ConsumeIntegralInRange<int>(0, str1.size());
+ str1.find(str1.c_str(), start_index);
+ },
+
+ // Path handling
+ [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
+ str1.getBasePath();
+ },
+ [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
+ str1.getPathExtension();
+ },
+ [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
+ str1.getPathLeaf();
+ },
+ [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
+ str1.getPathDir();
+ },
+ [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
+ str1.convertToResPath();
+ },
+ [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
+ android::String8 path_out_str = android::String8();
+ str1.walkPath(&path_out_str);
+ path_out_str.clear();
+ },
+ [](FuzzedDataProvider& dataProvider, android::String8 str1,
+ android::String8) -> void {
+ str1.setPathName(dataProvider.ConsumeBytesWithTerminator<char>(5).data());
+ },
+ [](FuzzedDataProvider& dataProvider, android::String8 str1,
+ android::String8) -> void {
+ str1.appendPath(dataProvider.ConsumeBytesWithTerminator<char>(5).data());
+ },
+};
+
+void callFunc(uint8_t index, FuzzedDataProvider& dataProvider, android::String8 str1,
+ android::String8 str2) {
+ operations[index](dataProvider, str1, str2);
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider dataProvider(data, size);
+ // Generate vector lengths
+ const size_t kVecOneLen = dataProvider.ConsumeIntegralInRange<size_t>(1, MAX_STRING_BYTES);
+ const size_t kVecTwoLen = dataProvider.ConsumeIntegralInRange<size_t>(1, MAX_STRING_BYTES);
+ // Populate vectors
+ std::vector<char> vec = dataProvider.ConsumeBytesWithTerminator<char>(kVecOneLen);
+ std::vector<char> vec_two = dataProvider.ConsumeBytesWithTerminator<char>(kVecTwoLen);
+ // Create UTF-8 pointers
+ android::String8 str_one_utf8 = android::String8(vec.data());
+ android::String8 str_two_utf8 = android::String8(vec_two.data());
+
+ // Run operations against strings
+ int opsRun = 0;
+ while (dataProvider.remaining_bytes() > 0 && opsRun++ < MAX_OPERATIONS) {
+ uint8_t op = dataProvider.ConsumeIntegralInRange<uint8_t>(0, operations.size() - 1);
+ callFunc(op, dataProvider, str_one_utf8, str_two_utf8);
+ }
+
+ // Just to be extra sure these can be freed, we're going to explicitly clear
+ // them
+ str_one_utf8.clear();
+ str_two_utf8.clear();
+ return 0;
+}
diff --git a/libutils/Threads.cpp b/libutils/Threads.cpp
index 540dcf4..147db54 100644
--- a/libutils/Threads.cpp
+++ b/libutils/Threads.cpp
@@ -302,8 +302,7 @@
}
#if defined(__ANDROID__)
-int androidSetThreadPriority(pid_t tid, int pri)
-{
+int androidSetThreadPriority(pid_t tid, int pri, bool change_policy) {
int rc = 0;
int lasterr = 0;
int curr_pri = getpriority(PRIO_PROCESS, tid);
@@ -312,17 +311,19 @@
return rc;
}
- if (pri >= ANDROID_PRIORITY_BACKGROUND) {
- rc = SetTaskProfiles(tid, {"SCHED_SP_BACKGROUND"}, true) ? 0 : -1;
- } else if (curr_pri >= ANDROID_PRIORITY_BACKGROUND) {
- SchedPolicy policy = SP_FOREGROUND;
- // Change to the sched policy group of the process.
- get_sched_policy(getpid(), &policy);
- rc = SetTaskProfiles(tid, {get_sched_policy_profile_name(policy)}, true) ? 0 : -1;
- }
+ if (change_policy) {
+ if (pri >= ANDROID_PRIORITY_BACKGROUND) {
+ rc = SetTaskProfiles(tid, {"SCHED_SP_BACKGROUND"}, true) ? 0 : -1;
+ } else if (curr_pri >= ANDROID_PRIORITY_BACKGROUND) {
+ SchedPolicy policy = SP_FOREGROUND;
+ // Change to the sched policy group of the process.
+ get_sched_policy(getpid(), &policy);
+ rc = SetTaskProfiles(tid, {get_sched_policy_profile_name(policy)}, true) ? 0 : -1;
+ }
- if (rc) {
- lasterr = errno;
+ if (rc) {
+ lasterr = errno;
+ }
}
if (setpriority(PRIO_PROCESS, tid, pri) < 0) {
diff --git a/libutils/Timers.cpp b/libutils/Timers.cpp
index 1172ae7..fd3f4a9 100644
--- a/libutils/Timers.cpp
+++ b/libutils/Timers.cpp
@@ -20,31 +20,37 @@
#include <utils/Timers.h>
#include <limits.h>
+#include <stdlib.h>
#include <time.h>
-// host linux support requires Linux 2.6.39+
+#include <android-base/macros.h>
+
+static constexpr size_t clock_id_max = 5;
+
+static void checkClockId(int clock) {
+ if (clock < 0 || clock >= clock_id_max) abort();
+}
+
#if defined(__linux__)
-nsecs_t systemTime(int clock)
-{
- static const clockid_t clocks[] = {
- CLOCK_REALTIME,
- CLOCK_MONOTONIC,
- CLOCK_PROCESS_CPUTIME_ID,
- CLOCK_THREAD_CPUTIME_ID,
- CLOCK_BOOTTIME
- };
- struct timespec t;
- t.tv_sec = t.tv_nsec = 0;
+nsecs_t systemTime(int clock) {
+ checkClockId(clock);
+ static constexpr clockid_t clocks[] = {CLOCK_REALTIME, CLOCK_MONOTONIC,
+ CLOCK_PROCESS_CPUTIME_ID, CLOCK_THREAD_CPUTIME_ID,
+ CLOCK_BOOTTIME};
+ static_assert(clock_id_max == arraysize(clocks));
+ timespec t = {};
clock_gettime(clocks[clock], &t);
return nsecs_t(t.tv_sec)*1000000000LL + t.tv_nsec;
}
#else
-nsecs_t systemTime(int /*clock*/)
-{
+nsecs_t systemTime(int clock) {
+ // TODO: is this ever called with anything but REALTIME on mac/windows?
+ checkClockId(clock);
+
// Clock support varies widely across hosts. Mac OS doesn't support
- // CLOCK_BOOTTIME, and Windows is windows.
- struct timeval t;
- t.tv_sec = t.tv_usec = 0;
+ // CLOCK_BOOTTIME (and doesn't even have clock_gettime until 10.12).
+ // Windows is windows.
+ timeval t = {};
gettimeofday(&t, nullptr);
return nsecs_t(t.tv_sec)*1000000000LL + nsecs_t(t.tv_usec)*1000LL;
}
diff --git a/libutils/Timers_test.cpp b/libutils/Timers_test.cpp
new file mode 100644
index 0000000..ec0051e
--- /dev/null
+++ b/libutils/Timers_test.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 <utils/Timers.h>
+
+#include <gtest/gtest.h>
+
+TEST(Timers, systemTime_invalid) {
+ EXPECT_EXIT(systemTime(-1), testing::KilledBySignal(SIGABRT), "");
+ systemTime(SYSTEM_TIME_REALTIME);
+ systemTime(SYSTEM_TIME_MONOTONIC);
+ systemTime(SYSTEM_TIME_PROCESS);
+ systemTime(SYSTEM_TIME_THREAD);
+ systemTime(SYSTEM_TIME_BOOTTIME);
+ EXPECT_EXIT(systemTime(SYSTEM_TIME_BOOTTIME + 1), testing::KilledBySignal(SIGABRT), "");
+}
diff --git a/libutils/Vector_fuzz.cpp b/libutils/Vector_fuzz.cpp
new file mode 100644
index 0000000..f6df051
--- /dev/null
+++ b/libutils/Vector_fuzz.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright 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 "fuzzer/FuzzedDataProvider.h"
+#include "utils/Vector.h"
+static constexpr uint16_t MAX_VEC_SIZE = 5000;
+
+void runVectorFuzz(const uint8_t* data, size_t size) {
+ FuzzedDataProvider dataProvider(data, size);
+ android::Vector<uint8_t> vec = android::Vector<uint8_t>();
+ // We want to test handling of sizeof as well.
+ android::Vector<uint32_t> vec32 = android::Vector<uint32_t>();
+
+ // We're going to generate two vectors of this size
+ size_t vectorSize = dataProvider.ConsumeIntegralInRange<size_t>(0, MAX_VEC_SIZE);
+ vec.setCapacity(vectorSize);
+ vec32.setCapacity(vectorSize);
+ for (size_t i = 0; i < vectorSize; i++) {
+ uint8_t count = dataProvider.ConsumeIntegralInRange<uint8_t>(1, 5);
+ vec.insertAt((uint8_t)i, i, count);
+ vec32.insertAt((uint32_t)i, i, count);
+ vec.push_front(i);
+ vec32.push(i);
+ }
+
+ // Now we'll perform some test operations with any remaining data
+ // Index to perform operations at
+ size_t index = dataProvider.ConsumeIntegralInRange<size_t>(0, vec.size());
+ std::vector<uint8_t> remainingVec = dataProvider.ConsumeRemainingBytes<uint8_t>();
+ // Insert an array and vector
+ vec.insertArrayAt(remainingVec.data(), index, remainingVec.size());
+ android::Vector<uint8_t> vecCopy = android::Vector<uint8_t>(vec);
+ vec.insertVectorAt(vecCopy, index);
+ // Same thing for 32 bit vector
+ android::Vector<uint32_t> vec32Copy = android::Vector<uint32_t>(vec32);
+ vec32.insertArrayAt(vec32Copy.array(), index, vec32.size());
+ vec32.insertVectorAt(vec32Copy, index);
+ // Replace single character
+ if (remainingVec.size() > 0) {
+ vec.replaceAt(remainingVec[0], index);
+ vec32.replaceAt(static_cast<uint32_t>(remainingVec[0]), index);
+ } else {
+ vec.replaceAt(0, index);
+ vec32.replaceAt(0, index);
+ }
+ // Add any remaining bytes
+ for (uint8_t i : remainingVec) {
+ vec.add(i);
+ vec32.add(static_cast<uint32_t>(i));
+ }
+ // Shrink capactiy
+ vec.setCapacity(remainingVec.size());
+ vec32.setCapacity(remainingVec.size());
+ // Iterate through each pointer
+ size_t sum = 0;
+ for (auto& it : vec) {
+ sum += it;
+ }
+ for (auto& it : vec32) {
+ sum += it;
+ }
+ // Cleanup
+ vec.clear();
+ vecCopy.clear();
+ vec32.clear();
+ vec32Copy.clear();
+}
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ runVectorFuzz(data, size);
+ return 0;
+}
diff --git a/libutils/include/utils/AndroidThreads.h b/libutils/include/utils/AndroidThreads.h
index a8d7851..3c30a2a 100644
--- a/libutils/include/utils/AndroidThreads.h
+++ b/libutils/include/utils/AndroidThreads.h
@@ -78,7 +78,9 @@
// should be one of the ANDROID_PRIORITY constants. Returns INVALID_OPERATION
// if the priority set failed, else another value if just the group set failed;
// in either case errno is set. Thread ID zero means current thread.
-extern int androidSetThreadPriority(pid_t tid, int prio);
+// Parameter "change_policy" indicates if sched policy should be changed. It needs
+// not be checked again if the change is done elsewhere like activity manager.
+extern int androidSetThreadPriority(pid_t tid, int prio, bool change_policy = true);
// Get the current priority of a particular thread. Returns one of the
// ANDROID_PRIORITY constants or a negative result in case of error.
diff --git a/libutils/include/utils/Compat.h b/libutils/include/utils/Compat.h
index dee577e..6002567 100644
--- a/libutils/include/utils/Compat.h
+++ b/libutils/include/utils/Compat.h
@@ -19,12 +19,20 @@
#include <unistd.h>
+#if !defined(__MINGW32__)
+#include <sys/mman.h>
+#endif
+
#if defined(__APPLE__)
/* Mac OS has always had a 64-bit off_t, so it doesn't have off64_t. */
-
+static_assert(sizeof(off_t) >= 8, "This code requires that Mac OS have at least a 64-bit off_t.");
typedef off_t off64_t;
+static inline void* mmap64(void* addr, size_t length, int prot, int flags, int fd, off64_t offset) {
+ return mmap(addr, length, prot, flags, fd, offset);
+}
+
static inline off64_t lseek64(int fd, off64_t offset, int whence) {
return lseek(fd, offset, whence);
}
diff --git a/libutils/include/utils/Looper.h b/libutils/include/utils/Looper.h
index c439c5c..466fbb7 100644
--- a/libutils/include/utils/Looper.h
+++ b/libutils/include/utils/Looper.h
@@ -26,6 +26,8 @@
#include <android-base/unique_fd.h>
+#include <utility>
+
namespace android {
/*
@@ -438,9 +440,8 @@
struct MessageEnvelope {
MessageEnvelope() : uptime(0) { }
- MessageEnvelope(nsecs_t u, const sp<MessageHandler> h,
- const Message& m) : uptime(u), handler(h), message(m) {
- }
+ MessageEnvelope(nsecs_t u, sp<MessageHandler> h, const Message& m)
+ : uptime(u), handler(std::move(h)), message(m) {}
nsecs_t uptime;
sp<MessageHandler> handler;
diff --git a/libutils/include/utils/Timers.h b/libutils/include/utils/Timers.h
index 54ec474..197fc26 100644
--- a/libutils/include/utils/Timers.h
+++ b/libutils/include/utils/Timers.h
@@ -14,11 +14,7 @@
* limitations under the License.
*/
-//
-// Timer functions.
-//
-#ifndef _LIBS_UTILS_TIMERS_H
-#define _LIBS_UTILS_TIMERS_H
+#pragma once
#include <stdint.h>
#include <sys/types.h>
@@ -77,11 +73,11 @@
static CONSTEXPR inline nsecs_t microseconds(nsecs_t v) { return us2ns(v); }
enum {
- SYSTEM_TIME_REALTIME = 0, // system-wide realtime clock
- SYSTEM_TIME_MONOTONIC = 1, // monotonic time since unspecified starting point
- SYSTEM_TIME_PROCESS = 2, // high-resolution per-process clock
- SYSTEM_TIME_THREAD = 3, // high-resolution per-thread clock
- SYSTEM_TIME_BOOTTIME = 4 // same as SYSTEM_TIME_MONOTONIC, but including CPU suspend time
+ SYSTEM_TIME_REALTIME = 0, // system-wide realtime clock
+ SYSTEM_TIME_MONOTONIC = 1, // monotonic time since unspecified starting point
+ SYSTEM_TIME_PROCESS = 2, // high-resolution per-process clock
+ SYSTEM_TIME_THREAD = 3, // high-resolution per-thread clock
+ SYSTEM_TIME_BOOTTIME = 4, // same as SYSTEM_TIME_MONOTONIC, but including CPU suspend time
};
// return the system-time according to the specified clock
@@ -104,5 +100,3 @@
#ifdef __cplusplus
} // extern "C"
#endif
-
-#endif // _LIBS_UTILS_TIMERS_H
diff --git a/libziparchive/.clang-format b/libziparchive/.clang-format
deleted file mode 120000
index fd0645f..0000000
--- a/libziparchive/.clang-format
+++ /dev/null
@@ -1 +0,0 @@
-../.clang-format-2
\ No newline at end of file
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
deleted file mode 100644
index 1bbffaf..0000000
--- a/libziparchive/Android.bp
+++ /dev/null
@@ -1,212 +0,0 @@
-//
-// Copyright (C) 2013 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-cc_defaults {
- name: "libziparchive_flags",
- cflags: [
- // ZLIB_CONST turns on const for input buffers, which is pretty standard.
- "-DZLIB_CONST",
- "-Werror",
- "-Wall",
- "-D_FILE_OFFSET_BITS=64",
- ],
- cppflags: [
- // Incorrectly warns when C++11 empty brace {} initializer is used.
- // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61489
- "-Wno-missing-field-initializers",
- "-Wconversion",
- "-Wno-sign-conversion",
- ],
-
- // Enable -Wold-style-cast only for non-Windows targets. _islower_l,
- // _isupper_l etc. in MinGW locale_win32.h (included from
- // libcxx/include/__locale) has an old-style-cast.
- target: {
- not_windows: {
- cppflags: [
- "-Wold-style-cast",
- ],
- },
- },
- sanitize: {
- misc_undefined: [
- "signed-integer-overflow",
- "unsigned-integer-overflow",
- "shift",
- "integer-divide-by-zero",
- "implicit-signed-integer-truncation",
- // TODO: Fix crash when we enable this option
- // "implicit-unsigned-integer-truncation",
- // TODO: not tested yet.
- // "implicit-integer-sign-change",
- ],
- },
-}
-
-cc_defaults {
- name: "libziparchive_defaults",
- srcs: [
- "zip_archive.cc",
- "zip_archive_stream_entry.cc",
- "zip_writer.cc",
- ],
-
- target: {
- windows: {
- cflags: ["-mno-ms-bitfields"],
-
- enabled: true,
- },
- },
-
- shared_libs: [
- "libbase",
- "liblog",
- ],
-
- // for FRIEND_TEST
- static_libs: ["libgtest_prod"],
- export_static_lib_headers: ["libgtest_prod"],
-
- export_include_dirs: ["include"],
-}
-
-cc_library {
- name: "libziparchive",
- host_supported: true,
- vendor_available: true,
- recovery_available: true,
- native_bridge_supported: true,
- vndk: {
- enabled: true,
- },
- double_loadable: true,
- export_shared_lib_headers: ["libbase"],
-
- defaults: [
- "libziparchive_defaults",
- "libziparchive_flags",
- ],
- shared_libs: [
- "liblog",
- "libbase",
- "libz",
- ],
- target: {
- linux_bionic: {
- enabled: true,
- },
- },
-}
-
-// Tests.
-cc_test {
- name: "ziparchive-tests",
- host_supported: true,
- defaults: ["libziparchive_flags"],
-
- data: [
- "testdata/**/*",
- ],
-
- srcs: [
- "entry_name_utils_test.cc",
- "zip_archive_test.cc",
- "zip_writer_test.cc",
- ],
- shared_libs: [
- "libbase",
- "liblog",
- ],
-
- static_libs: [
- "libziparchive",
- "libz",
- "libutils",
- ],
-
- target: {
- host: {
- cppflags: ["-Wno-unnamed-type-template-args"],
- },
- windows: {
- enabled: true,
- },
- },
- test_suites: ["device-tests"],
-}
-
-// Performance benchmarks.
-cc_benchmark {
- name: "ziparchive-benchmarks",
- defaults: ["libziparchive_flags"],
-
- srcs: [
- "zip_archive_benchmark.cpp",
- ],
- shared_libs: [
- "libbase",
- "liblog",
- ],
-
- static_libs: [
- "libziparchive",
- "libz",
- "libutils",
- ],
-
- target: {
- host: {
- cppflags: ["-Wno-unnamed-type-template-args"],
- },
- },
-}
-
-cc_binary {
- name: "ziptool",
- defaults: ["libziparchive_flags"],
- srcs: ["ziptool.cpp"],
- shared_libs: [
- "libbase",
- "libziparchive",
- ],
- recovery_available: true,
- host_supported: true,
- target: {
- android: {
- symlinks: ["unzip", "zipinfo"],
- },
- },
-}
-
-cc_fuzz {
- name: "libziparchive_fuzzer",
- srcs: ["libziparchive_fuzzer.cpp"],
- static_libs: ["libziparchive", "libbase", "libz", "liblog"],
- host_supported: true,
- corpus: ["testdata/*"],
-}
-
-sh_test {
- name: "ziptool-tests",
- src: "run-ziptool-tests-on-android.sh",
- filename: "run-ziptool-tests-on-android.sh",
- test_suites: ["general-tests"],
- host_supported: true,
- device_supported: false,
- test_config: "ziptool-tests.xml",
- data: ["cli-tests/**/*"],
- target_required: ["cli-test", "ziptool"],
-}
diff --git a/libziparchive/OWNERS b/libziparchive/OWNERS
deleted file mode 100644
index fcc567a..0000000
--- a/libziparchive/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-narayan@google.com
diff --git a/libziparchive/cli-tests/files/example.zip b/libziparchive/cli-tests/files/example.zip
deleted file mode 100644
index c3292e9..0000000
--- a/libziparchive/cli-tests/files/example.zip
+++ /dev/null
Binary files differ
diff --git a/libziparchive/cli-tests/unzip.test b/libziparchive/cli-tests/unzip.test
deleted file mode 100755
index 6e5cbf2..0000000
--- a/libziparchive/cli-tests/unzip.test
+++ /dev/null
@@ -1,148 +0,0 @@
-# unzip tests.
-
-# Note: since "master key", Android uses libziparchive for all zip file
-# handling, and that scans the whole central directory immediately. Not only
-# lookups by name but also iteration is implemented using the resulting hash
-# table, meaning that any test that makes assumptions about iteration order
-# will fail on Android.
-
-name: unzip -l
-command: unzip -l $FILES/example.zip d1/d2/x.txt
-after: [ ! -f d1/d2/x.txt ]
-expected-stdout:
- Archive: $FILES/example.zip
- Length Date Time Name
- --------- ---------- ----- ----
- 1024 2017-06-04 08:45 d1/d2/x.txt
- --------- -------
- 1024 1 file
----
-
-name: unzip -lq
-command: unzip -lq $FILES/example.zip d1/d2/x.txt
-after: [ ! -f d1/d2/x.txt ]
-expected-stdout:
- Length Date Time Name
- --------- ---------- ----- ----
- 1024 2017-06-04 08:45 d1/d2/x.txt
- --------- -------
- 1024 1 file
----
-
-name: unzip -lv
-command: unzip -lv $FILES/example.zip d1/d2/x.txt
-after: [ ! -f d1/d2/file ]
-expected-stdout:
- Archive: $FILES/example.zip
- Length Method Size Cmpr Date Time CRC-32 Name
- -------- ------ ------- ---- ---------- ----- -------- ----
- 1024 Defl:N 11 99% 2017-06-04 08:45 48d7f063 d1/d2/x.txt
- -------- ------- --- -------
- 1024 11 99% 1 file
----
-
-name: unzip -v
-command: unzip -v $FILES/example.zip d1/d2/x.txt
-after: [ ! -f d1/d2/file ]
-expected-stdout:
- Archive: $FILES/example.zip
- Length Method Size Cmpr Date Time CRC-32 Name
- -------- ------ ------- ---- ---------- ----- -------- ----
- 1024 Defl:N 11 99% 2017-06-04 08:45 48d7f063 d1/d2/x.txt
- -------- ------- --- -------
- 1024 11 99% 1 file
----
-
-name: unzip one file
-command: unzip -q $FILES/example.zip d1/d2/a.txt && cat d1/d2/a.txt
-after: [ ! -f d1/d2/b.txt ]
-expected-stdout:
- a
----
-
-name: unzip all files
-command: unzip -q $FILES/example.zip
-after: [ -f d1/d2/a.txt ]
-after: [ -f d1/d2/b.txt ]
-after: [ -f d1/d2/c.txt ]
-after: [ -f d1/d2/empty.txt ]
-after: [ -f d1/d2/x.txt ]
-after: [ -d d1/d2/dir ]
-expected-stdout:
----
-
-name: unzip -o
-before: mkdir -p d1/d2
-before: echo b > d1/d2/a.txt
-command: unzip -q -o $FILES/example.zip d1/d2/a.txt && cat d1/d2/a.txt
-expected-stdout:
- a
----
-
-name: unzip -n
-before: mkdir -p d1/d2
-before: echo b > d1/d2/a.txt
-command: unzip -q -n $FILES/example.zip d1/d2/a.txt && cat d1/d2/a.txt
-expected-stdout:
- b
----
-
-# The reference implementation will create *one* level of missing directories,
-# so this succeeds.
-name: unzip -d shallow non-existent
-command: unzip -q -d will-be-created $FILES/example.zip d1/d2/a.txt
-after: [ -d will-be-created ]
-after: [ -f will-be-created/d1/d2/a.txt ]
----
-
-# The reference implementation will *only* create one level of missing
-# directories, so this fails.
-name: unzip -d deep non-existent
-command: unzip -q -d oh-no/will-not-be-created $FILES/example.zip d1/d2/a.txt 2> stderr ; echo $? > status
-after: [ ! -d oh-no ]
-after: [ ! -d oh-no/will-not-be-created ]
-after: [ ! -f oh-no/will-not-be-created/d1/d2/a.txt ]
-after: grep -q "oh-no/will-not-be-created" stderr
-after: grep -q "No such file or directory" stderr
-# The reference implementation has *lots* of non-zero exit values, but we stick to 0 and 1.
-after: [ $(cat status) -gt 0 ]
----
-
-name: unzip -d exists
-before: mkdir dir
-command: unzip -q -d dir $FILES/example.zip d1/d2/a.txt && cat dir/d1/d2/a.txt
-after: [ ! -f d1/d2/a.txt ]
-expected-stdout:
- a
----
-
-name: unzip -p
-command: unzip -p $FILES/example.zip d1/d2/a.txt
-after: [ ! -f d1/d2/a.txt ]
-expected-stdout:
- a
----
-
-name: unzip -x FILE...
-# Note: the RI ignores -x DIR for some reason, but it's not obvious we should.
-command: unzip -q $FILES/example.zip -x d1/d2/a.txt d1/d2/b.txt d1/d2/empty.txt d1/d2/x.txt && cat d1/d2/c.txt
-after: [ ! -f d1/d2/a.txt ]
-after: [ ! -f d1/d2/b.txt ]
-after: [ ! -f d1/d2/empty.txt ]
-after: [ ! -f d1/d2/x.txt ]
-after: [ -d d1/d2/dir ]
-expected-stdout:
- ccc
----
-
-name: unzip FILE -x FILE...
-command: unzip -q $FILES/example.zip d1/d2/a.txt d1/d2/b.txt -x d1/d2/a.txt && cat d1/d2/b.txt
-after: [ ! -f d1/d2/a.txt ]
-after: [ -f d1/d2/b.txt ]
-after: [ ! -f d1/d2/c.txt ]
-after: [ ! -f d1/d2/empty.txt ]
-after: [ ! -f d1/d2/x.txt ]
-after: [ ! -d d1/d2/dir ]
-expected-stdout:
- bb
----
diff --git a/libziparchive/cli-tests/zipinfo.test b/libziparchive/cli-tests/zipinfo.test
deleted file mode 100755
index d5bce1c..0000000
--- a/libziparchive/cli-tests/zipinfo.test
+++ /dev/null
@@ -1,53 +0,0 @@
-# zipinfo tests.
-
-# Note: since "master key", Android uses libziparchive for all zip file
-# handling, and that scans the whole central directory immediately. Not only
-# lookups by name but also iteration is implemented using the resulting hash
-# table, meaning that any test that makes assumptions about iteration order
-# will fail on Android.
-
-name: zipinfo -1
-command: zipinfo -1 $FILES/example.zip | sort
-expected-stdout:
- d1/
- d1/d2/a.txt
- d1/d2/b.txt
- d1/d2/c.txt
- d1/d2/dir/
- d1/d2/empty.txt
- d1/d2/x.txt
----
-
-name: zipinfo header
-command: zipinfo $FILES/example.zip | head -2
-expected-stdout:
- Archive: $FILES/example.zip
- Zip file size: 1082 bytes, number of entries: 7
----
-
-name: zipinfo footer
-command: zipinfo $FILES/example.zip | tail -1
-expected-stdout:
- 7 files, 1033 bytes uncompressed, 20 bytes compressed: 98.1%
----
-
-name: zipinfo directory
-# The RI doesn't use ISO dates.
-command: zipinfo $FILES/example.zip d1/ | sed s/17-Jun-/2017-06-/
-expected-stdout:
- drwxr-x--- 3.0 unx 0 bx stor 2017-06-04 08:40 d1/
----
-
-name: zipinfo stored
-# The RI doesn't use ISO dates.
-command: zipinfo $FILES/example.zip d1/d2/empty.txt | sed s/17-Jun-/2017-06-/
-expected-stdout:
- -rw-r----- 3.0 unx 0 bx stor 2017-06-04 08:43 d1/d2/empty.txt
----
-
-name: zipinfo deflated
-# The RI doesn't use ISO dates.
-command: zipinfo $FILES/example.zip d1/d2/x.txt | sed s/17-Jun-/2017-06-/
-expected-stdout:
- -rw-r----- 3.0 unx 1024 tx defN 2017-06-04 08:45 d1/d2/x.txt
----
diff --git a/libziparchive/entry_name_utils-inl.h b/libziparchive/entry_name_utils-inl.h
deleted file mode 100644
index 10311b5..0000000
--- a/libziparchive/entry_name_utils-inl.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef LIBZIPARCHIVE_ENTRY_NAME_UTILS_INL_H_
-#define LIBZIPARCHIVE_ENTRY_NAME_UTILS_INL_H_
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <limits>
-
-// Check if |length| bytes at |entry_name| constitute a valid entry name.
-// Entry names must be valid UTF-8 and must not contain '0'. They also must
-// fit into the central directory record.
-inline bool IsValidEntryName(const uint8_t* entry_name, const size_t length) {
- if (length > std::numeric_limits<uint16_t>::max()) {
- return false;
- }
- for (size_t i = 0; i < length; ++i) {
- const uint8_t byte = entry_name[i];
- if (byte == 0) {
- return false;
- } else if ((byte & 0x80) == 0) {
- // Single byte sequence.
- continue;
- } else if ((byte & 0xc0) == 0x80 || (byte & 0xfe) == 0xfe) {
- // Invalid sequence.
- return false;
- } else {
- // 2-5 byte sequences.
- for (uint8_t first = static_cast<uint8_t>((byte & 0x7f) << 1); first & 0x80;
- first = static_cast<uint8_t>((first & 0x7f) << 1)) {
- ++i;
-
- // Missing continuation byte..
- if (i == length) {
- return false;
- }
-
- // Invalid continuation byte.
- const uint8_t continuation_byte = entry_name[i];
- if ((continuation_byte & 0xc0) != 0x80) {
- return false;
- }
- }
- }
- }
-
- return true;
-}
-
-#endif // LIBZIPARCHIVE_ENTRY_NAME_UTILS_INL_H_
diff --git a/libziparchive/entry_name_utils_test.cc b/libziparchive/entry_name_utils_test.cc
deleted file mode 100644
index d83d854..0000000
--- a/libziparchive/entry_name_utils_test.cc
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "entry_name_utils-inl.h"
-
-#include <gtest/gtest.h>
-
-TEST(entry_name_utils, NullChars) {
- // 'A', 'R', '\0', 'S', 'E'
- const uint8_t zeroes[] = {0x41, 0x52, 0x00, 0x53, 0x45};
- ASSERT_FALSE(IsValidEntryName(zeroes, sizeof(zeroes)));
-
- const uint8_t zeroes_continuation_chars[] = {0xc2, 0xa1, 0xc2, 0x00};
- ASSERT_FALSE(IsValidEntryName(zeroes_continuation_chars, sizeof(zeroes_continuation_chars)));
-}
-
-TEST(entry_name_utils, InvalidSequence) {
- // 0xfe is an invalid start byte
- const uint8_t invalid[] = {0x41, 0xfe};
- ASSERT_FALSE(IsValidEntryName(invalid, sizeof(invalid)));
-
- // 0x91 is an invalid start byte (it's a valid continuation byte).
- const uint8_t invalid2[] = {0x41, 0x91};
- ASSERT_FALSE(IsValidEntryName(invalid2, sizeof(invalid2)));
-}
-
-TEST(entry_name_utils, TruncatedContinuation) {
- // Malayalam script with truncated bytes. There should be 2 bytes
- // after 0xe0
- const uint8_t truncated[] = {0xe0, 0xb4, 0x85, 0xe0, 0xb4};
- ASSERT_FALSE(IsValidEntryName(truncated, sizeof(truncated)));
-
- // 0xc2 is the start of a 2 byte sequence that we've subsequently
- // dropped.
- const uint8_t truncated2[] = {0xc2, 0xc2, 0xa1};
- ASSERT_FALSE(IsValidEntryName(truncated2, sizeof(truncated2)));
-}
-
-TEST(entry_name_utils, BadContinuation) {
- // 0x41 is an invalid continuation char, since it's MSBs
- // aren't "10..." (are 01).
- const uint8_t bad[] = {0xc2, 0xa1, 0xc2, 0x41};
- ASSERT_FALSE(IsValidEntryName(bad, sizeof(bad)));
-
- // 0x41 is an invalid continuation char, since it's MSBs
- // aren't "10..." (are 11).
- const uint8_t bad2[] = {0xc2, 0xa1, 0xc2, 0xfe};
- ASSERT_FALSE(IsValidEntryName(bad2, sizeof(bad2)));
-}
diff --git a/libziparchive/include/ziparchive/zip_archive.h b/libziparchive/include/ziparchive/zip_archive.h
deleted file mode 100644
index 047af90..0000000
--- a/libziparchive/include/ziparchive/zip_archive.h
+++ /dev/null
@@ -1,279 +0,0 @@
-/*
- * Copyright (C) 2013 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
-
-/*
- * Read-only access to Zip archives, with minimal heap allocation.
- */
-
-#include <stdint.h>
-#include <string.h>
-#include <sys/cdefs.h>
-#include <sys/types.h>
-
-#include <string>
-#include <string_view>
-
-#include "android-base/off64_t.h"
-
-/* Zip compression methods we support */
-enum {
- kCompressStored = 0, // no compression
- kCompressDeflated = 8, // standard deflate
-};
-
-/*
- * Represents information about a zip entry in a zip file.
- */
-struct ZipEntry {
- // Compression method. One of kCompressStored or kCompressDeflated.
- // See also `gpbf` for deflate subtypes.
- uint16_t method;
-
- // Modification time. The zipfile format specifies
- // that the first two little endian bytes contain the time
- // and the last two little endian bytes contain the date.
- // See `GetModificationTime`.
- // TODO: should be overridden by extra time field, if present.
- uint32_t mod_time;
-
- // Returns `mod_time` as a broken-down struct tm.
- struct tm GetModificationTime() const;
-
- // Suggested Unix mode for this entry, from the zip archive if created on
- // Unix, or a default otherwise. See also `external_file_attributes`.
- mode_t unix_mode;
-
- // 1 if this entry contains a data descriptor segment, 0
- // otherwise.
- uint8_t has_data_descriptor;
-
- // Crc32 value of this ZipEntry. This information might
- // either be stored in the local file header or in a special
- // Data descriptor footer at the end of the file entry.
- uint32_t crc32;
-
- // Compressed length of this ZipEntry. Might be present
- // either in the local file header or in the data descriptor
- // footer.
- uint32_t compressed_length;
-
- // Uncompressed length of this ZipEntry. Might be present
- // either in the local file header or in the data descriptor
- // footer.
- uint32_t uncompressed_length;
-
- // The offset to the start of data for this ZipEntry.
- off64_t offset;
-
- // The version of zip and the host file system this came from (for zipinfo).
- uint16_t version_made_by;
-
- // The raw attributes, whose interpretation depends on the host
- // file system in `version_made_by` (for zipinfo). See also `unix_mode`.
- uint32_t external_file_attributes;
-
- // Specifics about the deflation (for zipinfo).
- uint16_t gpbf;
- // Whether this entry is believed to be text or binary (for zipinfo).
- bool is_text;
-};
-
-struct ZipArchive;
-typedef ZipArchive* ZipArchiveHandle;
-
-/*
- * Open a Zip archive, and sets handle to the value of the opaque
- * handle for the file. This handle must be released by calling
- * CloseArchive with this handle.
- *
- * Returns 0 on success, and negative values on failure.
- */
-int32_t OpenArchive(const char* fileName, ZipArchiveHandle* handle);
-
-/*
- * Like OpenArchive, but takes a file descriptor open for reading
- * at the start of the file. The descriptor must be mappable (this does
- * not allow access to a stream).
- *
- * Sets handle to the value of the opaque handle for this file descriptor.
- * This handle must be released by calling CloseArchive with this handle.
- *
- * If assume_ownership parameter is 'true' calling CloseArchive will close
- * the file.
- *
- * This function maps and scans the central directory and builds a table
- * of entries for future lookups.
- *
- * "debugFileName" will appear in error messages, but is not otherwise used.
- *
- * Returns 0 on success, and negative values on failure.
- */
-int32_t OpenArchiveFd(const int fd, const char* debugFileName, ZipArchiveHandle* handle,
- bool assume_ownership = true);
-
-int32_t OpenArchiveFromMemory(const void* address, size_t length, const char* debugFileName,
- ZipArchiveHandle* handle);
-/*
- * Close archive, releasing resources associated with it. This will
- * unmap the central directory of the zipfile and free all internal
- * data structures associated with the file. It is an error to use
- * this handle for any further operations without an intervening
- * call to one of the OpenArchive variants.
- */
-void CloseArchive(ZipArchiveHandle archive);
-
-/** See GetArchiveInfo(). */
-struct ZipArchiveInfo {
- /** The size in bytes of the archive itself. Used by zipinfo. */
- off64_t archive_size;
- /** The number of entries in the archive. */
- size_t entry_count;
-};
-
-/**
- * Returns information about the given archive.
- */
-ZipArchiveInfo GetArchiveInfo(ZipArchiveHandle archive);
-
-/*
- * Find an entry in the Zip archive, by name. |data| must be non-null.
- *
- * Returns 0 if an entry is found, and populates |data| with information
- * about this entry. Returns negative values otherwise.
- *
- * It's important to note that |data->crc32|, |data->compLen| and
- * |data->uncompLen| might be set to values from the central directory
- * if this file entry contains a data descriptor footer. To verify crc32s
- * and length, a call to VerifyCrcAndLengths must be made after entry data
- * has been processed.
- *
- * On non-Windows platforms this method does not modify internal state and
- * can be called concurrently.
- */
-int32_t FindEntry(const ZipArchiveHandle archive, const std::string_view entryName, ZipEntry* data);
-
-/*
- * Start iterating over all entries of a zip file. The order of iteration
- * is not guaranteed to be the same as the order of elements
- * in the central directory but is stable for a given zip file. |cookie| will
- * contain the value of an opaque cookie which can be used to make one or more
- * calls to Next. All calls to StartIteration must be matched by a call to
- * EndIteration to free any allocated memory.
- *
- * This method also accepts optional prefix and suffix to restrict iteration to
- * entry names that start with |optional_prefix| or end with |optional_suffix|.
- *
- * Returns 0 on success and negative values on failure.
- */
-int32_t StartIteration(ZipArchiveHandle archive, void** cookie_ptr,
- const std::string_view optional_prefix = "",
- const std::string_view optional_suffix = "");
-
-/*
- * Advance to the next element in the zipfile in iteration order.
- *
- * Returns 0 on success, -1 if there are no more elements in this
- * archive and lower negative values on failure.
- */
-int32_t Next(void* cookie, ZipEntry* data, std::string* name);
-int32_t Next(void* cookie, ZipEntry* data, std::string_view* name);
-
-/*
- * End iteration over all entries of a zip file and frees the memory allocated
- * in StartIteration.
- */
-void EndIteration(void* cookie);
-
-/*
- * Uncompress and write an entry to an open file identified by |fd|.
- * |entry->uncompressed_length| bytes will be written to the file at
- * its current offset, and the file will be truncated at the end of
- * the uncompressed data (no truncation if |fd| references a block
- * device).
- *
- * Returns 0 on success and negative values on failure.
- */
-int32_t ExtractEntryToFile(ZipArchiveHandle archive, ZipEntry* entry, int fd);
-
-/**
- * Uncompress a given zip entry to the memory region at |begin| and of
- * size |size|. This size is expected to be the same as the *declared*
- * uncompressed length of the zip entry. It is an error if the *actual*
- * number of uncompressed bytes differs from this number.
- *
- * Returns 0 on success and negative values on failure.
- */
-int32_t ExtractToMemory(ZipArchiveHandle archive, ZipEntry* entry, uint8_t* begin, uint32_t size);
-
-int GetFileDescriptor(const ZipArchiveHandle archive);
-
-const char* ErrorCodeString(int32_t error_code);
-
-#if !defined(_WIN32)
-typedef bool (*ProcessZipEntryFunction)(const uint8_t* buf, size_t buf_size, void* cookie);
-
-/*
- * Stream the uncompressed data through the supplied function,
- * passing cookie to it each time it gets called.
- */
-int32_t ProcessZipEntryContents(ZipArchiveHandle archive, ZipEntry* entry,
- ProcessZipEntryFunction func, void* cookie);
-#endif
-
-namespace zip_archive {
-
-class Writer {
- public:
- virtual bool Append(uint8_t* buf, size_t buf_size) = 0;
- virtual ~Writer();
-
- protected:
- Writer() = default;
-
- private:
- Writer(const Writer&) = delete;
- void operator=(const Writer&) = delete;
-};
-
-class Reader {
- public:
- virtual bool ReadAtOffset(uint8_t* buf, size_t len, uint32_t offset) const = 0;
- virtual ~Reader();
-
- protected:
- Reader() = default;
-
- private:
- Reader(const Reader&) = delete;
- void operator=(const Reader&) = delete;
-};
-
-/*
- * Inflates the first |compressed_length| bytes of |reader| to a given |writer|.
- * |crc_out| is set to the CRC32 checksum of the uncompressed data.
- *
- * Returns 0 on success and negative values on failure, for example if |reader|
- * cannot supply the right amount of data, or if the number of bytes written to
- * data does not match |uncompressed_length|.
- *
- * If |crc_out| is not nullptr, it is set to the crc32 checksum of the
- * uncompressed data.
- */
-int32_t Inflate(const Reader& reader, const uint32_t compressed_length,
- const uint32_t uncompressed_length, Writer* writer, uint64_t* crc_out);
-} // namespace zip_archive
diff --git a/libziparchive/include/ziparchive/zip_archive_stream_entry.h b/libziparchive/include/ziparchive/zip_archive_stream_entry.h
deleted file mode 100644
index 8c6ca79..0000000
--- a/libziparchive/include/ziparchive/zip_archive_stream_entry.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// Read-only stream access to Zip archives entries.
-#pragma once
-
-#include <ziparchive/zip_archive.h>
-
-#include <vector>
-
-#include "android-base/off64_t.h"
-
-class ZipArchiveStreamEntry {
- public:
- virtual ~ZipArchiveStreamEntry() {}
-
- virtual const std::vector<uint8_t>* Read() = 0;
-
- virtual bool Verify() = 0;
-
- static ZipArchiveStreamEntry* Create(ZipArchiveHandle handle, const ZipEntry& entry);
- static ZipArchiveStreamEntry* CreateRaw(ZipArchiveHandle handle, const ZipEntry& entry);
-
- protected:
- ZipArchiveStreamEntry(ZipArchiveHandle handle) : handle_(handle) {}
-
- virtual bool Init(const ZipEntry& entry);
-
- ZipArchiveHandle handle_;
-
- off64_t offset_ = 0;
- uint32_t crc32_ = 0u;
-};
diff --git a/libziparchive/include/ziparchive/zip_writer.h b/libziparchive/include/ziparchive/zip_writer.h
deleted file mode 100644
index d68683d..0000000
--- a/libziparchive/include/ziparchive/zip_writer.h
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdio>
-#include <ctime>
-
-#include <gtest/gtest_prod.h>
-#include <memory>
-#include <string>
-#include <string_view>
-#include <vector>
-
-#include "android-base/macros.h"
-#include "android-base/off64_t.h"
-
-struct z_stream_s;
-typedef struct z_stream_s z_stream;
-
-/**
- * Writes a Zip file via a stateful interface.
- *
- * Example:
- *
- * FILE* file = fopen("path/to/zip.zip", "wb");
- *
- * ZipWriter writer(file);
- *
- * writer.StartEntry("test.txt", ZipWriter::kCompress | ZipWriter::kAlign);
- * writer.WriteBytes(buffer, bufferLen);
- * writer.WriteBytes(buffer2, bufferLen2);
- * writer.FinishEntry();
- *
- * writer.StartEntry("empty.txt", 0);
- * writer.FinishEntry();
- *
- * writer.Finish();
- *
- * fclose(file);
- */
-class ZipWriter {
- public:
- enum {
- /**
- * Flag to compress the zip entry using deflate.
- */
- kCompress = 0x01,
-
- /**
- * Flag to align the zip entry data on a 32bit boundary. Useful for
- * mmapping the data at runtime.
- */
- kAlign32 = 0x02,
- };
-
- /**
- * A struct representing a zip file entry.
- */
- struct FileEntry {
- std::string path;
- uint16_t compression_method;
- uint32_t crc32;
- uint32_t compressed_size;
- uint32_t uncompressed_size;
- uint16_t last_mod_time;
- uint16_t last_mod_date;
- uint16_t padding_length;
- off64_t local_file_header_offset;
- };
-
- static const char* ErrorCodeString(int32_t error_code);
-
- /**
- * Create a ZipWriter that will write into a FILE stream. The file should be opened with
- * open mode of "wb" or "w+b". ZipWriter does not take ownership of the file stream. The
- * caller is responsible for closing the file.
- */
- explicit ZipWriter(FILE* f);
-
- // Move constructor.
- ZipWriter(ZipWriter&& zipWriter) noexcept;
-
- // Move assignment.
- ZipWriter& operator=(ZipWriter&& zipWriter) noexcept;
-
- /**
- * Starts a new zip entry with the given path and flags.
- * Flags can be a bitwise OR of ZipWriter::kCompress and ZipWriter::kAlign.
- * Subsequent calls to WriteBytes(const void*, size_t) will add data to this entry.
- * Returns 0 on success, and an error value < 0 on failure.
- */
- int32_t StartEntry(std::string_view path, size_t flags);
-
- /**
- * Starts a new zip entry with the given path and flags, where the
- * entry will be aligned to the given alignment.
- * Flags can only be ZipWriter::kCompress. Using the flag ZipWriter::kAlign32
- * will result in an error.
- * Subsequent calls to WriteBytes(const void*, size_t) will add data to this entry.
- * Returns 0 on success, and an error value < 0 on failure.
- */
- int32_t StartAlignedEntry(std::string_view path, size_t flags, uint32_t alignment);
-
- /**
- * Same as StartEntry(const char*, size_t), but sets a last modified time for the entry.
- */
- int32_t StartEntryWithTime(std::string_view path, size_t flags, time_t time);
-
- /**
- * Same as StartAlignedEntry(const char*, size_t), but sets a last modified time for the entry.
- */
- int32_t StartAlignedEntryWithTime(std::string_view path, size_t flags, time_t time, uint32_t alignment);
-
- /**
- * Writes bytes to the zip file for the previously started zip entry.
- * Returns 0 on success, and an error value < 0 on failure.
- */
- int32_t WriteBytes(const void* data, size_t len);
-
- /**
- * Finish a zip entry started with StartEntry(const char*, size_t) or
- * StartEntryWithTime(const char*, size_t, time_t). This must be called before
- * any new zip entries are started, or before Finish() is called.
- * Returns 0 on success, and an error value < 0 on failure.
- */
- int32_t FinishEntry();
-
- /**
- * Discards the last-written entry. Can only be called after an entry has been written using
- * FinishEntry().
- * Returns 0 on success, and an error value < 0 on failure.
- */
- int32_t DiscardLastEntry();
-
- /**
- * Sets `out_entry` to the last entry written after a call to FinishEntry().
- * Returns 0 on success, and an error value < 0 if no entries have been written.
- */
- int32_t GetLastEntry(FileEntry* out_entry);
-
- /**
- * Writes the Central Directory Headers and flushes the zip file stream.
- * Returns 0 on success, and an error value < 0 on failure.
- */
- int32_t Finish();
-
- private:
- DISALLOW_COPY_AND_ASSIGN(ZipWriter);
-
- int32_t HandleError(int32_t error_code);
- int32_t PrepareDeflate();
- int32_t StoreBytes(FileEntry* file, const void* data, uint32_t len);
- int32_t CompressBytes(FileEntry* file, const void* data, uint32_t len);
- int32_t FlushCompressedBytes(FileEntry* file);
- bool ShouldUseDataDescriptor() const;
-
- enum class State {
- kWritingZip,
- kWritingEntry,
- kDone,
- kError,
- };
-
- FILE* file_;
- bool seekable_;
- off64_t current_offset_;
- State state_;
- std::vector<FileEntry> files_;
- FileEntry current_file_entry_;
-
- std::unique_ptr<z_stream, void (*)(z_stream*)> z_stream_;
- std::vector<uint8_t> buffer_;
-
- FRIEND_TEST(zipwriter, WriteToUnseekableFile);
-};
diff --git a/libziparchive/libziparchive_fuzzer.cpp b/libziparchive/libziparchive_fuzzer.cpp
deleted file mode 100644
index 75e7939..0000000
--- a/libziparchive/libziparchive_fuzzer.cpp
+++ /dev/null
@@ -1,13 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <ziparchive/zip_archive.h>
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- ZipArchiveHandle handle = nullptr;
- OpenArchiveFromMemory(data, size, "fuzz", &handle);
- CloseArchive(handle);
- return 0;
-}
diff --git a/libziparchive/run-ziptool-tests-on-android.sh b/libziparchive/run-ziptool-tests-on-android.sh
deleted file mode 100755
index 3c23d43..0000000
--- a/libziparchive/run-ziptool-tests-on-android.sh
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/bin/bash
-
-# Copy the tests across.
-adb shell rm -rf /data/local/tmp/ziptool-tests/
-adb shell mkdir /data/local/tmp/ziptool-tests/
-adb push cli-tests/ /data/local/tmp/ziptool-tests/
-#adb push cli-test /data/local/tmp/ziptool-tests/
-
-if tty -s; then
- dash_t="-t"
-else
- dash_t=""
-fi
-
-exec adb shell $dash_t cli-test /data/local/tmp/ziptool-tests/cli-tests/*.test
diff --git a/libziparchive/testdata/bad_crc.zip b/libziparchive/testdata/bad_crc.zip
deleted file mode 100644
index e12ba07..0000000
--- a/libziparchive/testdata/bad_crc.zip
+++ /dev/null
Binary files differ
diff --git a/libziparchive/testdata/bad_filename.zip b/libziparchive/testdata/bad_filename.zip
deleted file mode 100644
index 294eaf5..0000000
--- a/libziparchive/testdata/bad_filename.zip
+++ /dev/null
Binary files differ
diff --git a/libziparchive/testdata/crash.apk b/libziparchive/testdata/crash.apk
deleted file mode 100644
index d6dd52d..0000000
--- a/libziparchive/testdata/crash.apk
+++ /dev/null
Binary files differ
diff --git a/libziparchive/testdata/declaredlength.zip b/libziparchive/testdata/declaredlength.zip
deleted file mode 100644
index 773380c..0000000
--- a/libziparchive/testdata/declaredlength.zip
+++ /dev/null
Binary files differ
diff --git a/libziparchive/testdata/dummy-update.zip b/libziparchive/testdata/dummy-update.zip
deleted file mode 100644
index 6976bf1..0000000
--- a/libziparchive/testdata/dummy-update.zip
+++ /dev/null
Binary files differ
diff --git a/libziparchive/testdata/empty.zip b/libziparchive/testdata/empty.zip
deleted file mode 100644
index 15cb0ec..0000000
--- a/libziparchive/testdata/empty.zip
+++ /dev/null
Binary files differ
diff --git a/libziparchive/testdata/large.zip b/libziparchive/testdata/large.zip
deleted file mode 100644
index 49659c8..0000000
--- a/libziparchive/testdata/large.zip
+++ /dev/null
Binary files differ
diff --git a/libziparchive/testdata/valid.zip b/libziparchive/testdata/valid.zip
deleted file mode 100644
index 9e7cb78..0000000
--- a/libziparchive/testdata/valid.zip
+++ /dev/null
Binary files differ
diff --git a/libziparchive/testdata/zero-size-cd.zip b/libziparchive/testdata/zero-size-cd.zip
deleted file mode 100644
index b6c8cbe..0000000
--- a/libziparchive/testdata/zero-size-cd.zip
+++ /dev/null
Binary files differ
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
deleted file mode 100644
index 68837cc..0000000
--- a/libziparchive/zip_archive.cc
+++ /dev/null
@@ -1,1250 +0,0 @@
-/*
- * 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.
- */
-
-/*
- * Read-only access to Zip archives, with minimal heap allocation.
- */
-
-#define LOG_TAG "ziparchive"
-
-#include "ziparchive/zip_archive.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <memory>
-#include <vector>
-
-#if defined(__APPLE__)
-#define lseek64 lseek
-#endif
-
-#if defined(__BIONIC__)
-#include <android/fdsan.h>
-#endif
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/macros.h> // TEMP_FAILURE_RETRY may or may not be in unistd
-#include <android-base/mapped_file.h>
-#include <android-base/memory.h>
-#include <android-base/strings.h>
-#include <android-base/utf8.h>
-#include <log/log.h>
-#include "zlib.h"
-
-#include "entry_name_utils-inl.h"
-#include "zip_archive_common.h"
-#include "zip_archive_private.h"
-
-using android::base::get_unaligned;
-
-// Used to turn on crc checks - verify that the content CRC matches the values
-// specified in the local file header and the central directory.
-static const bool kCrcChecksEnabled = false;
-
-// The maximum number of bytes to scan backwards for the EOCD start.
-static const uint32_t kMaxEOCDSearch = kMaxCommentLen + sizeof(EocdRecord);
-
-/*
- * A Read-only Zip archive.
- *
- * We want "open" and "find entry by name" to be fast operations, and
- * we want to use as little memory as possible. We memory-map the zip
- * central directory, and load a hash table with pointers to the filenames
- * (which aren't null-terminated). The other fields are at a fixed offset
- * from the filename, so we don't need to extract those (but we do need
- * to byte-read and endian-swap them every time we want them).
- *
- * It's possible that somebody has handed us a massive (~1GB) zip archive,
- * so we can't expect to mmap the entire file.
- *
- * To speed comparisons when doing a lookup by name, we could make the mapping
- * "private" (copy-on-write) and null-terminate the filenames after verifying
- * the record structure. However, this requires a private mapping of
- * every page that the Central Directory touches. Easier to tuck a copy
- * of the string length into the hash table entry.
- */
-
-/*
- * Round up to the next highest power of 2.
- *
- * Found on http://graphics.stanford.edu/~seander/bithacks.html.
- */
-static uint32_t RoundUpPower2(uint32_t val) {
- val--;
- val |= val >> 1;
- val |= val >> 2;
- val |= val >> 4;
- val |= val >> 8;
- val |= val >> 16;
- val++;
-
- return val;
-}
-
-static uint32_t ComputeHash(std::string_view name) {
- return static_cast<uint32_t>(std::hash<std::string_view>{}(name));
-}
-
-/*
- * Convert a ZipEntry to a hash table index, verifying that it's in a
- * valid range.
- */
-static int64_t EntryToIndex(const ZipStringOffset* hash_table, const uint32_t hash_table_size,
- std::string_view name, const uint8_t* start) {
- const uint32_t hash = ComputeHash(name);
-
- // NOTE: (hash_table_size - 1) is guaranteed to be non-negative.
- uint32_t ent = hash & (hash_table_size - 1);
- while (hash_table[ent].name_offset != 0) {
- if (hash_table[ent].ToStringView(start) == name) {
- return ent;
- }
- ent = (ent + 1) & (hash_table_size - 1);
- }
-
- ALOGV("Zip: Unable to find entry %.*s", static_cast<int>(name.size()), name.data());
- return kEntryNotFound;
-}
-
-/*
- * Add a new entry to the hash table.
- */
-static int32_t AddToHash(ZipStringOffset* hash_table, const uint32_t hash_table_size,
- std::string_view name, const uint8_t* start) {
- const uint64_t hash = ComputeHash(name);
- uint32_t ent = hash & (hash_table_size - 1);
-
- /*
- * We over-allocated the table, so we're guaranteed to find an empty slot.
- * Further, we guarantee that the hashtable size is not 0.
- */
- while (hash_table[ent].name_offset != 0) {
- if (hash_table[ent].ToStringView(start) == name) {
- // We've found a duplicate entry. We don't accept duplicates.
- ALOGW("Zip: Found duplicate entry %.*s", static_cast<int>(name.size()), name.data());
- return kDuplicateEntry;
- }
- ent = (ent + 1) & (hash_table_size - 1);
- }
-
- // `name` has already been validated before entry.
- const char* start_char = reinterpret_cast<const char*>(start);
- hash_table[ent].name_offset = static_cast<uint32_t>(name.data() - start_char);
- hash_table[ent].name_length = static_cast<uint16_t>(name.size());
- return 0;
-}
-
-#if defined(__BIONIC__)
-uint64_t GetOwnerTag(const ZipArchive* archive) {
- return android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_ZIPARCHIVE,
- reinterpret_cast<uint64_t>(archive));
-}
-#endif
-
-ZipArchive::ZipArchive(const int fd, bool assume_ownership)
- : mapped_zip(fd),
- close_file(assume_ownership),
- directory_offset(0),
- central_directory(),
- directory_map(),
- num_entries(0),
- hash_table_size(0),
- hash_table(nullptr) {
-#if defined(__BIONIC__)
- if (assume_ownership) {
- android_fdsan_exchange_owner_tag(fd, 0, GetOwnerTag(this));
- }
-#endif
-}
-
-ZipArchive::ZipArchive(const void* address, size_t length)
- : mapped_zip(address, length),
- close_file(false),
- directory_offset(0),
- central_directory(),
- directory_map(),
- num_entries(0),
- hash_table_size(0),
- hash_table(nullptr) {}
-
-ZipArchive::~ZipArchive() {
- if (close_file && mapped_zip.GetFileDescriptor() >= 0) {
-#if defined(__BIONIC__)
- android_fdsan_close_with_tag(mapped_zip.GetFileDescriptor(), GetOwnerTag(this));
-#else
- close(mapped_zip.GetFileDescriptor());
-#endif
- }
-
- free(hash_table);
-}
-
-static int32_t MapCentralDirectory0(const char* debug_file_name, ZipArchive* archive,
- off64_t file_length, uint32_t read_amount,
- uint8_t* scan_buffer) {
- const off64_t search_start = file_length - read_amount;
-
- if (!archive->mapped_zip.ReadAtOffset(scan_buffer, read_amount, search_start)) {
- ALOGE("Zip: read %" PRId64 " from offset %" PRId64 " failed", static_cast<int64_t>(read_amount),
- static_cast<int64_t>(search_start));
- return kIoError;
- }
-
- /*
- * Scan backward for the EOCD magic. In an archive without a trailing
- * comment, we'll find it on the first try. (We may want to consider
- * doing an initial minimal read; if we don't find it, retry with a
- * second read as above.)
- */
- CHECK_LE(read_amount, std::numeric_limits<int32_t>::max());
- int32_t i = read_amount - sizeof(EocdRecord);
- for (; i >= 0; i--) {
- if (scan_buffer[i] == 0x50) {
- uint32_t* sig_addr = reinterpret_cast<uint32_t*>(&scan_buffer[i]);
- if (get_unaligned<uint32_t>(sig_addr) == EocdRecord::kSignature) {
- ALOGV("+++ Found EOCD at buf+%d", i);
- break;
- }
- }
- }
- if (i < 0) {
- ALOGD("Zip: EOCD not found, %s is not zip", debug_file_name);
- return kInvalidFile;
- }
-
- const off64_t eocd_offset = search_start + i;
- const EocdRecord* eocd = reinterpret_cast<const EocdRecord*>(scan_buffer + i);
- /*
- * Verify that there's no trailing space at the end of the central directory
- * and its comment.
- */
- const off64_t calculated_length = eocd_offset + sizeof(EocdRecord) + eocd->comment_length;
- if (calculated_length != file_length) {
- ALOGW("Zip: %" PRId64 " extraneous bytes at the end of the central directory",
- static_cast<int64_t>(file_length - calculated_length));
- return kInvalidFile;
- }
-
- /*
- * Grab the CD offset and size, and the number of entries in the
- * archive and verify that they look reasonable.
- */
- if (static_cast<off64_t>(eocd->cd_start_offset) + eocd->cd_size > eocd_offset) {
- ALOGW("Zip: bad offsets (dir %" PRIu32 ", size %" PRIu32 ", eocd %" PRId64 ")",
- eocd->cd_start_offset, eocd->cd_size, static_cast<int64_t>(eocd_offset));
- return kInvalidOffset;
- }
- if (eocd->num_records == 0) {
-#if defined(__ANDROID__)
- ALOGW("Zip: empty archive?");
-#endif
- return kEmptyArchive;
- }
-
- ALOGV("+++ num_entries=%" PRIu32 " dir_size=%" PRIu32 " dir_offset=%" PRIu32, eocd->num_records,
- eocd->cd_size, eocd->cd_start_offset);
-
- // It all looks good. Create a mapping for the CD, and set the fields
- // in archive.
- if (!archive->InitializeCentralDirectory(static_cast<off64_t>(eocd->cd_start_offset),
- static_cast<size_t>(eocd->cd_size))) {
- return kMmapFailed;
- }
-
- archive->num_entries = eocd->num_records;
- archive->directory_offset = eocd->cd_start_offset;
-
- return 0;
-}
-
-/*
- * Find the zip Central Directory and memory-map it.
- *
- * On success, returns 0 after populating fields from the EOCD area:
- * directory_offset
- * directory_ptr
- * num_entries
- */
-static int32_t MapCentralDirectory(const char* debug_file_name, ZipArchive* archive) {
- // Test file length. We use lseek64 to make sure the file
- // is small enough to be a zip file (Its size must be less than
- // 0xffffffff bytes).
- off64_t file_length = archive->mapped_zip.GetFileLength();
- if (file_length == -1) {
- return kInvalidFile;
- }
-
- if (file_length > static_cast<off64_t>(0xffffffff)) {
- ALOGV("Zip: zip file too long %" PRId64, static_cast<int64_t>(file_length));
- return kInvalidFile;
- }
-
- if (file_length < static_cast<off64_t>(sizeof(EocdRecord))) {
- ALOGV("Zip: length %" PRId64 " is too small to be zip", static_cast<int64_t>(file_length));
- return kInvalidFile;
- }
-
- /*
- * Perform the traditional EOCD snipe hunt.
- *
- * We're searching for the End of Central Directory magic number,
- * which appears at the start of the EOCD block. It's followed by
- * 18 bytes of EOCD stuff and up to 64KB of archive comment. We
- * need to read the last part of the file into a buffer, dig through
- * it to find the magic number, parse some values out, and use those
- * to determine the extent of the CD.
- *
- * We start by pulling in the last part of the file.
- */
- uint32_t read_amount = kMaxEOCDSearch;
- if (file_length < read_amount) {
- read_amount = static_cast<uint32_t>(file_length);
- }
-
- std::vector<uint8_t> scan_buffer(read_amount);
- int32_t result =
- MapCentralDirectory0(debug_file_name, archive, file_length, read_amount, scan_buffer.data());
- return result;
-}
-
-/*
- * Parses the Zip archive's Central Directory. Allocates and populates the
- * hash table.
- *
- * Returns 0 on success.
- */
-static int32_t ParseZipArchive(ZipArchive* archive) {
- const uint8_t* const cd_ptr = archive->central_directory.GetBasePtr();
- const size_t cd_length = archive->central_directory.GetMapLength();
- const uint16_t num_entries = archive->num_entries;
-
- /*
- * Create hash table. We have a minimum 75% load factor, possibly as
- * low as 50% after we round off to a power of 2. There must be at
- * least one unused entry to avoid an infinite loop during creation.
- */
- archive->hash_table_size = RoundUpPower2(1 + (num_entries * 4) / 3);
- archive->hash_table =
- reinterpret_cast<ZipStringOffset*>(calloc(archive->hash_table_size, sizeof(ZipStringOffset)));
- if (archive->hash_table == nullptr) {
- ALOGW("Zip: unable to allocate the %u-entry hash_table, entry size: %zu",
- archive->hash_table_size, sizeof(ZipStringOffset));
- return kAllocationFailed;
- }
-
- /*
- * Walk through the central directory, adding entries to the hash
- * table and verifying values.
- */
- const uint8_t* const cd_end = cd_ptr + cd_length;
- const uint8_t* ptr = cd_ptr;
- for (uint16_t i = 0; i < num_entries; i++) {
- if (ptr > cd_end - sizeof(CentralDirectoryRecord)) {
- ALOGW("Zip: ran off the end (item #%" PRIu16 ", %zu bytes of central directory)", i,
- cd_length);
-#if defined(__ANDROID__)
- android_errorWriteLog(0x534e4554, "36392138");
-#endif
- return kInvalidFile;
- }
-
- const CentralDirectoryRecord* cdr = reinterpret_cast<const CentralDirectoryRecord*>(ptr);
- if (cdr->record_signature != CentralDirectoryRecord::kSignature) {
- ALOGW("Zip: missed a central dir sig (at %" PRIu16 ")", i);
- return kInvalidFile;
- }
-
- const off64_t local_header_offset = cdr->local_file_header_offset;
- if (local_header_offset >= archive->directory_offset) {
- ALOGW("Zip: bad LFH offset %" PRId64 " at entry %" PRIu16,
- static_cast<int64_t>(local_header_offset), i);
- return kInvalidFile;
- }
-
- const uint16_t file_name_length = cdr->file_name_length;
- const uint16_t extra_length = cdr->extra_field_length;
- const uint16_t comment_length = cdr->comment_length;
- const uint8_t* file_name = ptr + sizeof(CentralDirectoryRecord);
-
- if (file_name + file_name_length > cd_end) {
- ALOGW("Zip: file name for entry %" PRIu16
- " exceeds the central directory range, file_name_length: %" PRIu16 ", cd_length: %zu",
- i, file_name_length, cd_length);
- return kInvalidEntryName;
- }
- // Check that file name is valid UTF-8 and doesn't contain NUL (U+0000) characters.
- if (!IsValidEntryName(file_name, file_name_length)) {
- ALOGW("Zip: invalid file name at entry %" PRIu16, i);
- return kInvalidEntryName;
- }
-
- // Add the CDE filename to the hash table.
- std::string_view entry_name{reinterpret_cast<const char*>(file_name), file_name_length};
- const int add_result = AddToHash(archive->hash_table, archive->hash_table_size, entry_name,
- archive->central_directory.GetBasePtr());
- if (add_result != 0) {
- ALOGW("Zip: Error adding entry to hash table %d", add_result);
- return add_result;
- }
-
- ptr += sizeof(CentralDirectoryRecord) + file_name_length + extra_length + comment_length;
- if ((ptr - cd_ptr) > static_cast<int64_t>(cd_length)) {
- ALOGW("Zip: bad CD advance (%tu vs %zu) at entry %" PRIu16, ptr - cd_ptr, cd_length, i);
- return kInvalidFile;
- }
- }
-
- uint32_t lfh_start_bytes;
- if (!archive->mapped_zip.ReadAtOffset(reinterpret_cast<uint8_t*>(&lfh_start_bytes),
- sizeof(uint32_t), 0)) {
- ALOGW("Zip: Unable to read header for entry at offset == 0.");
- return kInvalidFile;
- }
-
- if (lfh_start_bytes != LocalFileHeader::kSignature) {
- ALOGW("Zip: Entry at offset zero has invalid LFH signature %" PRIx32, lfh_start_bytes);
-#if defined(__ANDROID__)
- android_errorWriteLog(0x534e4554, "64211847");
-#endif
- return kInvalidFile;
- }
-
- ALOGV("+++ zip good scan %" PRIu16 " entries", num_entries);
-
- return 0;
-}
-
-static int32_t OpenArchiveInternal(ZipArchive* archive, const char* debug_file_name) {
- int32_t result = MapCentralDirectory(debug_file_name, archive);
- return result != 0 ? result : ParseZipArchive(archive);
-}
-
-int32_t OpenArchiveFd(int fd, const char* debug_file_name, ZipArchiveHandle* handle,
- bool assume_ownership) {
- ZipArchive* archive = new ZipArchive(fd, assume_ownership);
- *handle = archive;
- return OpenArchiveInternal(archive, debug_file_name);
-}
-
-int32_t OpenArchive(const char* fileName, ZipArchiveHandle* handle) {
- const int fd = ::android::base::utf8::open(fileName, O_RDONLY | O_BINARY | O_CLOEXEC, 0);
- ZipArchive* archive = new ZipArchive(fd, true);
- *handle = archive;
-
- if (fd < 0) {
- ALOGW("Unable to open '%s': %s", fileName, strerror(errno));
- return kIoError;
- }
-
- return OpenArchiveInternal(archive, fileName);
-}
-
-int32_t OpenArchiveFromMemory(const void* address, size_t length, const char* debug_file_name,
- ZipArchiveHandle* handle) {
- ZipArchive* archive = new ZipArchive(address, length);
- *handle = archive;
- return OpenArchiveInternal(archive, debug_file_name);
-}
-
-ZipArchiveInfo GetArchiveInfo(ZipArchiveHandle archive) {
- ZipArchiveInfo result;
- result.archive_size = archive->mapped_zip.GetFileLength();
- result.entry_count = archive->num_entries;
- return result;
-}
-
-/*
- * Close a ZipArchive, closing the file and freeing the contents.
- */
-void CloseArchive(ZipArchiveHandle archive) {
- ALOGV("Closing archive %p", archive);
- delete archive;
-}
-
-static int32_t ValidateDataDescriptor(MappedZipFile& mapped_zip, ZipEntry* entry) {
- uint8_t ddBuf[sizeof(DataDescriptor) + sizeof(DataDescriptor::kOptSignature)];
- off64_t offset = entry->offset;
- if (entry->method != kCompressStored) {
- offset += entry->compressed_length;
- } else {
- offset += entry->uncompressed_length;
- }
-
- if (!mapped_zip.ReadAtOffset(ddBuf, sizeof(ddBuf), offset)) {
- return kIoError;
- }
-
- const uint32_t ddSignature = *(reinterpret_cast<const uint32_t*>(ddBuf));
- const uint16_t ddOffset = (ddSignature == DataDescriptor::kOptSignature) ? 4 : 0;
- const DataDescriptor* descriptor = reinterpret_cast<const DataDescriptor*>(ddBuf + ddOffset);
-
- // Validate that the values in the data descriptor match those in the central
- // directory.
- if (entry->compressed_length != descriptor->compressed_size ||
- entry->uncompressed_length != descriptor->uncompressed_size ||
- entry->crc32 != descriptor->crc32) {
- ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu32 ", %" PRIu32 ", %" PRIx32
- "}, was {%" PRIu32 ", %" PRIu32 ", %" PRIx32 "}",
- entry->compressed_length, entry->uncompressed_length, entry->crc32,
- descriptor->compressed_size, descriptor->uncompressed_size, descriptor->crc32);
- return kInconsistentInformation;
- }
-
- return 0;
-}
-
-static int32_t FindEntry(const ZipArchive* archive, const int32_t ent, ZipEntry* data) {
- const uint16_t nameLen = archive->hash_table[ent].name_length;
-
- // Recover the start of the central directory entry from the filename
- // pointer. The filename is the first entry past the fixed-size data,
- // so we can just subtract back from that.
- const uint8_t* base_ptr = archive->central_directory.GetBasePtr();
- const uint8_t* ptr = base_ptr + archive->hash_table[ent].name_offset;
- ptr -= sizeof(CentralDirectoryRecord);
-
- // This is the base of our mmapped region, we have to sanity check that
- // the name that's in the hash table is a pointer to a location within
- // this mapped region.
- if (ptr < base_ptr || ptr > base_ptr + archive->central_directory.GetMapLength()) {
- ALOGW("Zip: Invalid entry pointer");
- return kInvalidOffset;
- }
-
- const CentralDirectoryRecord* cdr = reinterpret_cast<const CentralDirectoryRecord*>(ptr);
-
- // The offset of the start of the central directory in the zipfile.
- // We keep this lying around so that we can sanity check all our lengths
- // and our per-file structures.
- const off64_t cd_offset = archive->directory_offset;
-
- // Fill out the compression method, modification time, crc32
- // and other interesting attributes from the central directory. These
- // will later be compared against values from the local file header.
- data->method = cdr->compression_method;
- data->mod_time = cdr->last_mod_date << 16 | cdr->last_mod_time;
- data->crc32 = cdr->crc32;
- data->compressed_length = cdr->compressed_size;
- data->uncompressed_length = cdr->uncompressed_size;
-
- // Figure out the local header offset from the central directory. The
- // actual file data will begin after the local header and the name /
- // extra comments.
- const off64_t local_header_offset = cdr->local_file_header_offset;
- if (local_header_offset + static_cast<off64_t>(sizeof(LocalFileHeader)) >= cd_offset) {
- ALOGW("Zip: bad local hdr offset in zip");
- return kInvalidOffset;
- }
-
- uint8_t lfh_buf[sizeof(LocalFileHeader)];
- if (!archive->mapped_zip.ReadAtOffset(lfh_buf, sizeof(lfh_buf), local_header_offset)) {
- ALOGW("Zip: failed reading lfh name from offset %" PRId64,
- static_cast<int64_t>(local_header_offset));
- return kIoError;
- }
-
- const LocalFileHeader* lfh = reinterpret_cast<const LocalFileHeader*>(lfh_buf);
-
- if (lfh->lfh_signature != LocalFileHeader::kSignature) {
- ALOGW("Zip: didn't find signature at start of lfh, offset=%" PRId64,
- static_cast<int64_t>(local_header_offset));
- return kInvalidOffset;
- }
-
- // Paranoia: Match the values specified in the local file header
- // to those specified in the central directory.
-
- // Warn if central directory and local file header don't agree on the use
- // of a trailing Data Descriptor. The reference implementation is inconsistent
- // and appears to use the LFH value during extraction (unzip) but the CD value
- // while displayng information about archives (zipinfo). The spec remains
- // silent on this inconsistency as well.
- //
- // For now, always use the version from the LFH but make sure that the values
- // specified in the central directory match those in the data descriptor.
- //
- // NOTE: It's also worth noting that unzip *does* warn about inconsistencies in
- // bit 11 (EFS: The language encoding flag, marking that filename and comment are
- // encoded using UTF-8). This implementation does not check for the presence of
- // that flag and always enforces that entry names are valid UTF-8.
- if ((lfh->gpb_flags & kGPBDDFlagMask) != (cdr->gpb_flags & kGPBDDFlagMask)) {
- ALOGW("Zip: gpb flag mismatch at bit 3. expected {%04" PRIx16 "}, was {%04" PRIx16 "}",
- cdr->gpb_flags, lfh->gpb_flags);
- }
-
- // If there is no trailing data descriptor, verify that the central directory and local file
- // header agree on the crc, compressed, and uncompressed sizes of the entry.
- if ((lfh->gpb_flags & kGPBDDFlagMask) == 0) {
- data->has_data_descriptor = 0;
- if (data->compressed_length != lfh->compressed_size ||
- data->uncompressed_length != lfh->uncompressed_size || data->crc32 != lfh->crc32) {
- ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu32 ", %" PRIu32 ", %" PRIx32
- "}, was {%" PRIu32 ", %" PRIu32 ", %" PRIx32 "}",
- data->compressed_length, data->uncompressed_length, data->crc32, lfh->compressed_size,
- lfh->uncompressed_size, lfh->crc32);
- return kInconsistentInformation;
- }
- } else {
- data->has_data_descriptor = 1;
- }
-
- // 4.4.2.1: the upper byte of `version_made_by` gives the source OS. Unix is 3.
- data->version_made_by = cdr->version_made_by;
- data->external_file_attributes = cdr->external_file_attributes;
- if ((data->version_made_by >> 8) == 3) {
- data->unix_mode = (cdr->external_file_attributes >> 16) & 0xffff;
- } else {
- data->unix_mode = 0777;
- }
-
- // 4.4.4: general purpose bit flags.
- data->gpbf = lfh->gpb_flags;
-
- // 4.4.14: the lowest bit of the internal file attributes field indicates text.
- // Currently only needed to implement zipinfo.
- data->is_text = (cdr->internal_file_attributes & 1);
-
- // Check that the local file header name matches the declared
- // name in the central directory.
- if (lfh->file_name_length != nameLen) {
- ALOGW("Zip: lfh name length did not match central directory");
- return kInconsistentInformation;
- }
- const off64_t name_offset = local_header_offset + sizeof(LocalFileHeader);
- if (name_offset + lfh->file_name_length > cd_offset) {
- ALOGW("Zip: lfh name has invalid declared length");
- return kInvalidOffset;
- }
- std::vector<uint8_t> name_buf(nameLen);
- if (!archive->mapped_zip.ReadAtOffset(name_buf.data(), nameLen, name_offset)) {
- ALOGW("Zip: failed reading lfh name from offset %" PRId64, static_cast<int64_t>(name_offset));
- return kIoError;
- }
- const std::string_view entry_name =
- archive->hash_table[ent].ToStringView(archive->central_directory.GetBasePtr());
- if (memcmp(entry_name.data(), name_buf.data(), nameLen) != 0) {
- ALOGW("Zip: lfh name did not match central directory");
- return kInconsistentInformation;
- }
-
- const off64_t data_offset = local_header_offset + sizeof(LocalFileHeader) +
- lfh->file_name_length + lfh->extra_field_length;
- if (data_offset > cd_offset) {
- ALOGW("Zip: bad data offset %" PRId64 " in zip", static_cast<int64_t>(data_offset));
- return kInvalidOffset;
- }
-
- if (static_cast<off64_t>(data_offset + data->compressed_length) > cd_offset) {
- ALOGW("Zip: bad compressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")",
- static_cast<int64_t>(data_offset), data->compressed_length,
- static_cast<int64_t>(cd_offset));
- return kInvalidOffset;
- }
-
- if (data->method == kCompressStored &&
- static_cast<off64_t>(data_offset + data->uncompressed_length) > cd_offset) {
- ALOGW("Zip: bad uncompressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")",
- static_cast<int64_t>(data_offset), data->uncompressed_length,
- static_cast<int64_t>(cd_offset));
- return kInvalidOffset;
- }
-
- data->offset = data_offset;
- return 0;
-}
-
-struct IterationHandle {
- ZipArchive* archive;
-
- std::string prefix;
- std::string suffix;
-
- uint32_t position = 0;
-
- IterationHandle(ZipArchive* archive, std::string_view in_prefix, std::string_view in_suffix)
- : archive(archive), prefix(in_prefix), suffix(in_suffix) {}
-};
-
-int32_t StartIteration(ZipArchiveHandle archive, void** cookie_ptr,
- const std::string_view optional_prefix,
- const std::string_view optional_suffix) {
- if (archive == NULL || archive->hash_table == NULL) {
- ALOGW("Zip: Invalid ZipArchiveHandle");
- return kInvalidHandle;
- }
-
- if (optional_prefix.size() > static_cast<size_t>(UINT16_MAX) ||
- optional_suffix.size() > static_cast<size_t>(UINT16_MAX)) {
- ALOGW("Zip: prefix/suffix too long");
- return kInvalidEntryName;
- }
-
- *cookie_ptr = new IterationHandle(archive, optional_prefix, optional_suffix);
- return 0;
-}
-
-void EndIteration(void* cookie) {
- delete reinterpret_cast<IterationHandle*>(cookie);
-}
-
-int32_t FindEntry(const ZipArchiveHandle archive, const std::string_view entryName,
- ZipEntry* data) {
- if (entryName.empty() || entryName.size() > static_cast<size_t>(UINT16_MAX)) {
- ALOGW("Zip: Invalid filename of length %zu", entryName.size());
- return kInvalidEntryName;
- }
-
- const int64_t ent = EntryToIndex(archive->hash_table, archive->hash_table_size, entryName,
- archive->central_directory.GetBasePtr());
- if (ent < 0) {
- ALOGV("Zip: Could not find entry %.*s", static_cast<int>(entryName.size()), entryName.data());
- return static_cast<int32_t>(ent); // kEntryNotFound is safe to truncate.
- }
- // We know there are at most hash_table_size entries, safe to truncate.
- return FindEntry(archive, static_cast<uint32_t>(ent), data);
-}
-
-int32_t Next(void* cookie, ZipEntry* data, std::string* name) {
- std::string_view sv;
- int32_t result = Next(cookie, data, &sv);
- if (result == 0 && name) {
- *name = std::string(sv);
- }
- return result;
-}
-
-int32_t Next(void* cookie, ZipEntry* data, std::string_view* name) {
- IterationHandle* handle = reinterpret_cast<IterationHandle*>(cookie);
- if (handle == NULL) {
- ALOGW("Zip: Null ZipArchiveHandle");
- return kInvalidHandle;
- }
-
- ZipArchive* archive = handle->archive;
- if (archive == NULL || archive->hash_table == NULL) {
- ALOGW("Zip: Invalid ZipArchiveHandle");
- return kInvalidHandle;
- }
-
- const uint32_t currentOffset = handle->position;
- const uint32_t hash_table_length = archive->hash_table_size;
- const ZipStringOffset* hash_table = archive->hash_table;
- for (uint32_t i = currentOffset; i < hash_table_length; ++i) {
- const std::string_view entry_name =
- hash_table[i].ToStringView(archive->central_directory.GetBasePtr());
- if (hash_table[i].name_offset != 0 && (android::base::StartsWith(entry_name, handle->prefix) &&
- android::base::EndsWith(entry_name, handle->suffix))) {
- handle->position = (i + 1);
- const int error = FindEntry(archive, i, data);
- if (!error && name) {
- *name = entry_name;
- }
- return error;
- }
- }
-
- handle->position = 0;
- return kIterationEnd;
-}
-
-// A Writer that writes data to a fixed size memory region.
-// The size of the memory region must be equal to the total size of
-// the data appended to it.
-class MemoryWriter : public zip_archive::Writer {
- public:
- MemoryWriter(uint8_t* buf, size_t size) : Writer(), buf_(buf), size_(size), bytes_written_(0) {}
-
- virtual bool Append(uint8_t* buf, size_t buf_size) override {
- if (bytes_written_ + buf_size > size_) {
- ALOGW("Zip: Unexpected size %zu (declared) vs %zu (actual)", size_,
- bytes_written_ + buf_size);
- return false;
- }
-
- memcpy(buf_ + bytes_written_, buf, buf_size);
- bytes_written_ += buf_size;
- return true;
- }
-
- private:
- uint8_t* const buf_;
- const size_t size_;
- size_t bytes_written_;
-};
-
-// A Writer that appends data to a file |fd| at its current position.
-// The file will be truncated to the end of the written data.
-class FileWriter : public zip_archive::Writer {
- public:
- // Creates a FileWriter for |fd| and prepare to write |entry| to it,
- // guaranteeing that the file descriptor is valid and that there's enough
- // space on the volume to write out the entry completely and that the file
- // is truncated to the correct length (no truncation if |fd| references a
- // block device).
- //
- // Returns a valid FileWriter on success, |nullptr| if an error occurred.
- static FileWriter Create(int fd, const ZipEntry* entry) {
- const uint32_t declared_length = entry->uncompressed_length;
- const off64_t current_offset = lseek64(fd, 0, SEEK_CUR);
- if (current_offset == -1) {
- ALOGW("Zip: unable to seek to current location on fd %d: %s", fd, strerror(errno));
- return FileWriter{};
- }
-
-#if defined(__linux__)
- if (declared_length > 0) {
- // Make sure we have enough space on the volume to extract the compressed
- // entry. Note that the call to ftruncate below will change the file size but
- // will not allocate space on disk and this call to fallocate will not
- // change the file size.
- // Note: fallocate is only supported by the following filesystems -
- // btrfs, ext4, ocfs2, and xfs. Therefore fallocate might fail with
- // EOPNOTSUPP error when issued in other filesystems.
- // Hence, check for the return error code before concluding that the
- // disk does not have enough space.
- long result = TEMP_FAILURE_RETRY(fallocate(fd, 0, current_offset, declared_length));
- if (result == -1 && errno == ENOSPC) {
- ALOGW("Zip: unable to allocate %" PRId64 " bytes at offset %" PRId64 ": %s",
- static_cast<int64_t>(declared_length), static_cast<int64_t>(current_offset),
- strerror(errno));
- return FileWriter{};
- }
- }
-#endif // __linux__
-
- struct stat sb;
- if (fstat(fd, &sb) == -1) {
- ALOGW("Zip: unable to fstat file: %s", strerror(errno));
- return FileWriter{};
- }
-
- // Block device doesn't support ftruncate(2).
- if (!S_ISBLK(sb.st_mode)) {
- long result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset));
- if (result == -1) {
- ALOGW("Zip: unable to truncate file to %" PRId64 ": %s",
- static_cast<int64_t>(declared_length + current_offset), strerror(errno));
- return FileWriter{};
- }
- }
-
- return FileWriter(fd, declared_length);
- }
-
- FileWriter(FileWriter&& other) noexcept
- : fd_(other.fd_),
- declared_length_(other.declared_length_),
- total_bytes_written_(other.total_bytes_written_) {
- other.fd_ = -1;
- }
-
- bool IsValid() const { return fd_ != -1; }
-
- virtual bool Append(uint8_t* buf, size_t buf_size) override {
- if (total_bytes_written_ + buf_size > declared_length_) {
- ALOGW("Zip: Unexpected size %zu (declared) vs %zu (actual)", declared_length_,
- total_bytes_written_ + buf_size);
- return false;
- }
-
- const bool result = android::base::WriteFully(fd_, buf, buf_size);
- if (result) {
- total_bytes_written_ += buf_size;
- } else {
- ALOGW("Zip: unable to write %zu bytes to file; %s", buf_size, strerror(errno));
- }
-
- return result;
- }
-
- private:
- explicit FileWriter(const int fd = -1, const size_t declared_length = 0)
- : Writer(), fd_(fd), declared_length_(declared_length), total_bytes_written_(0) {}
-
- int fd_;
- const size_t declared_length_;
- size_t total_bytes_written_;
-};
-
-class EntryReader : public zip_archive::Reader {
- public:
- EntryReader(const MappedZipFile& zip_file, const ZipEntry* entry)
- : Reader(), zip_file_(zip_file), entry_(entry) {}
-
- virtual bool ReadAtOffset(uint8_t* buf, size_t len, uint32_t offset) const {
- return zip_file_.ReadAtOffset(buf, len, entry_->offset + offset);
- }
-
- virtual ~EntryReader() {}
-
- private:
- const MappedZipFile& zip_file_;
- const ZipEntry* entry_;
-};
-
-// This method is using libz macros with old-style-casts
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wold-style-cast"
-static inline int zlib_inflateInit2(z_stream* stream, int window_bits) {
- return inflateInit2(stream, window_bits);
-}
-#pragma GCC diagnostic pop
-
-namespace zip_archive {
-
-// Moved out of line to avoid -Wweak-vtables.
-Reader::~Reader() {}
-Writer::~Writer() {}
-
-int32_t Inflate(const Reader& reader, const uint32_t compressed_length,
- const uint32_t uncompressed_length, Writer* writer, uint64_t* crc_out) {
- const size_t kBufSize = 32768;
- std::vector<uint8_t> read_buf(kBufSize);
- std::vector<uint8_t> write_buf(kBufSize);
- z_stream zstream;
- int zerr;
-
- /*
- * Initialize the zlib stream struct.
- */
- memset(&zstream, 0, sizeof(zstream));
- zstream.zalloc = Z_NULL;
- zstream.zfree = Z_NULL;
- zstream.opaque = Z_NULL;
- zstream.next_in = NULL;
- zstream.avail_in = 0;
- zstream.next_out = &write_buf[0];
- zstream.avail_out = kBufSize;
- zstream.data_type = Z_UNKNOWN;
-
- /*
- * Use the undocumented "negative window bits" feature to tell zlib
- * that there's no zlib header waiting for it.
- */
- zerr = zlib_inflateInit2(&zstream, -MAX_WBITS);
- if (zerr != Z_OK) {
- if (zerr == Z_VERSION_ERROR) {
- ALOGE("Installed zlib is not compatible with linked version (%s)", ZLIB_VERSION);
- } else {
- ALOGW("Call to inflateInit2 failed (zerr=%d)", zerr);
- }
-
- return kZlibError;
- }
-
- auto zstream_deleter = [](z_stream* stream) {
- inflateEnd(stream); /* free up any allocated structures */
- };
-
- std::unique_ptr<z_stream, decltype(zstream_deleter)> zstream_guard(&zstream, zstream_deleter);
-
- const bool compute_crc = (crc_out != nullptr);
- uLong crc = 0;
- uint32_t remaining_bytes = compressed_length;
- do {
- /* read as much as we can */
- if (zstream.avail_in == 0) {
- const uint32_t read_size = (remaining_bytes > kBufSize) ? kBufSize : remaining_bytes;
- const uint32_t offset = (compressed_length - remaining_bytes);
- // Make sure to read at offset to ensure concurrent access to the fd.
- if (!reader.ReadAtOffset(read_buf.data(), read_size, offset)) {
- ALOGW("Zip: inflate read failed, getSize = %u: %s", read_size, strerror(errno));
- return kIoError;
- }
-
- remaining_bytes -= read_size;
-
- zstream.next_in = &read_buf[0];
- zstream.avail_in = read_size;
- }
-
- /* uncompress the data */
- zerr = inflate(&zstream, Z_NO_FLUSH);
- if (zerr != Z_OK && zerr != Z_STREAM_END) {
- ALOGW("Zip: inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)", zerr, zstream.next_in,
- zstream.avail_in, zstream.next_out, zstream.avail_out);
- return kZlibError;
- }
-
- /* write when we're full or when we're done */
- if (zstream.avail_out == 0 || (zerr == Z_STREAM_END && zstream.avail_out != kBufSize)) {
- const size_t write_size = zstream.next_out - &write_buf[0];
- if (!writer->Append(&write_buf[0], write_size)) {
- return kIoError;
- } else if (compute_crc) {
- DCHECK_LE(write_size, kBufSize);
- crc = crc32(crc, &write_buf[0], static_cast<uint32_t>(write_size));
- }
-
- zstream.next_out = &write_buf[0];
- zstream.avail_out = kBufSize;
- }
- } while (zerr == Z_OK);
-
- CHECK_EQ(zerr, Z_STREAM_END); /* other errors should've been caught */
-
- // NOTE: zstream.adler is always set to 0, because we're using the -MAX_WBITS
- // "feature" of zlib to tell it there won't be a zlib file header. zlib
- // doesn't bother calculating the checksum in that scenario. We just do
- // it ourselves above because there are no additional gains to be made by
- // having zlib calculate it for us, since they do it by calling crc32 in
- // the same manner that we have above.
- if (compute_crc) {
- *crc_out = crc;
- }
-
- if (zstream.total_out != uncompressed_length || remaining_bytes != 0) {
- ALOGW("Zip: size mismatch on inflated file (%lu vs %" PRIu32 ")", zstream.total_out,
- uncompressed_length);
- return kInconsistentInformation;
- }
-
- return 0;
-}
-} // namespace zip_archive
-
-static int32_t InflateEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry* entry,
- zip_archive::Writer* writer, uint64_t* crc_out) {
- const EntryReader reader(mapped_zip, entry);
-
- return zip_archive::Inflate(reader, entry->compressed_length, entry->uncompressed_length, writer,
- crc_out);
-}
-
-static int32_t CopyEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry* entry,
- zip_archive::Writer* writer, uint64_t* crc_out) {
- static const uint32_t kBufSize = 32768;
- std::vector<uint8_t> buf(kBufSize);
-
- const uint32_t length = entry->uncompressed_length;
- uint32_t count = 0;
- uLong crc = 0;
- while (count < length) {
- uint32_t remaining = length - count;
- off64_t offset = entry->offset + count;
-
- // Safe conversion because kBufSize is narrow enough for a 32 bit signed value.
- const uint32_t block_size = (remaining > kBufSize) ? kBufSize : remaining;
-
- // Make sure to read at offset to ensure concurrent access to the fd.
- if (!mapped_zip.ReadAtOffset(buf.data(), block_size, offset)) {
- ALOGW("CopyFileToFile: copy read failed, block_size = %u, offset = %" PRId64 ": %s",
- block_size, static_cast<int64_t>(offset), strerror(errno));
- return kIoError;
- }
-
- if (!writer->Append(&buf[0], block_size)) {
- return kIoError;
- }
- crc = crc32(crc, &buf[0], block_size);
- count += block_size;
- }
-
- *crc_out = crc;
-
- return 0;
-}
-
-int32_t ExtractToWriter(ZipArchiveHandle archive, ZipEntry* entry, zip_archive::Writer* writer) {
- const uint16_t method = entry->method;
-
- // this should default to kUnknownCompressionMethod.
- int32_t return_value = -1;
- uint64_t crc = 0;
- if (method == kCompressStored) {
- return_value = CopyEntryToWriter(archive->mapped_zip, entry, writer, &crc);
- } else if (method == kCompressDeflated) {
- return_value = InflateEntryToWriter(archive->mapped_zip, entry, writer, &crc);
- }
-
- if (!return_value && entry->has_data_descriptor) {
- return_value = ValidateDataDescriptor(archive->mapped_zip, entry);
- if (return_value) {
- return return_value;
- }
- }
-
- // Validate that the CRC matches the calculated value.
- if (kCrcChecksEnabled && (entry->crc32 != static_cast<uint32_t>(crc))) {
- ALOGW("Zip: crc mismatch: expected %" PRIu32 ", was %" PRIu64, entry->crc32, crc);
- return kInconsistentInformation;
- }
-
- return return_value;
-}
-
-int32_t ExtractToMemory(ZipArchiveHandle archive, ZipEntry* entry, uint8_t* begin, uint32_t size) {
- MemoryWriter writer(begin, size);
- return ExtractToWriter(archive, entry, &writer);
-}
-
-int32_t ExtractEntryToFile(ZipArchiveHandle archive, ZipEntry* entry, int fd) {
- auto writer = FileWriter::Create(fd, entry);
- if (!writer.IsValid()) {
- return kIoError;
- }
-
- return ExtractToWriter(archive, entry, &writer);
-}
-
-const char* ErrorCodeString(int32_t error_code) {
- // Make sure that the number of entries in kErrorMessages and ErrorCodes
- // match.
- static_assert((-kLastErrorCode + 1) == arraysize(kErrorMessages),
- "(-kLastErrorCode + 1) != arraysize(kErrorMessages)");
-
- const uint32_t idx = -error_code;
- if (idx < arraysize(kErrorMessages)) {
- return kErrorMessages[idx];
- }
-
- return "Unknown return code";
-}
-
-int GetFileDescriptor(const ZipArchiveHandle archive) {
- return archive->mapped_zip.GetFileDescriptor();
-}
-
-#if !defined(_WIN32)
-class ProcessWriter : public zip_archive::Writer {
- public:
- ProcessWriter(ProcessZipEntryFunction func, void* cookie)
- : Writer(), proc_function_(func), cookie_(cookie) {}
-
- virtual bool Append(uint8_t* buf, size_t buf_size) override {
- return proc_function_(buf, buf_size, cookie_);
- }
-
- private:
- ProcessZipEntryFunction proc_function_;
- void* cookie_;
-};
-
-int32_t ProcessZipEntryContents(ZipArchiveHandle archive, ZipEntry* entry,
- ProcessZipEntryFunction func, void* cookie) {
- ProcessWriter writer(func, cookie);
- return ExtractToWriter(archive, entry, &writer);
-}
-
-#endif //! defined(_WIN32)
-
-int MappedZipFile::GetFileDescriptor() const {
- if (!has_fd_) {
- ALOGW("Zip: MappedZipFile doesn't have a file descriptor.");
- return -1;
- }
- return fd_;
-}
-
-const void* MappedZipFile::GetBasePtr() const {
- if (has_fd_) {
- ALOGW("Zip: MappedZipFile doesn't have a base pointer.");
- return nullptr;
- }
- return base_ptr_;
-}
-
-off64_t MappedZipFile::GetFileLength() const {
- if (has_fd_) {
- off64_t result = lseek64(fd_, 0, SEEK_END);
- if (result == -1) {
- ALOGE("Zip: lseek on fd %d failed: %s", fd_, strerror(errno));
- }
- return result;
- } else {
- if (base_ptr_ == nullptr) {
- ALOGE("Zip: invalid file map");
- return -1;
- }
- return static_cast<off64_t>(data_length_);
- }
-}
-
-// Attempts to read |len| bytes into |buf| at offset |off|.
-bool MappedZipFile::ReadAtOffset(uint8_t* buf, size_t len, off64_t off) const {
- if (has_fd_) {
- if (!android::base::ReadFullyAtOffset(fd_, buf, len, off)) {
- ALOGE("Zip: failed to read at offset %" PRId64, off);
- return false;
- }
- } else {
- if (off < 0 || off > static_cast<off64_t>(data_length_)) {
- ALOGE("Zip: invalid offset: %" PRId64 ", data length: %" PRId64, off, data_length_);
- return false;
- }
- memcpy(buf, static_cast<const uint8_t*>(base_ptr_) + off, len);
- }
- return true;
-}
-
-void CentralDirectory::Initialize(const void* map_base_ptr, off64_t cd_start_offset,
- size_t cd_size) {
- base_ptr_ = static_cast<const uint8_t*>(map_base_ptr) + cd_start_offset;
- length_ = cd_size;
-}
-
-bool ZipArchive::InitializeCentralDirectory(off64_t cd_start_offset, size_t cd_size) {
- if (mapped_zip.HasFd()) {
- directory_map = android::base::MappedFile::FromFd(mapped_zip.GetFileDescriptor(),
- cd_start_offset, cd_size, PROT_READ);
- if (!directory_map) {
- ALOGE("Zip: failed to map central directory (offset %" PRId64 ", size %zu): %s",
- cd_start_offset, cd_size, strerror(errno));
- return false;
- }
-
- CHECK_EQ(directory_map->size(), cd_size);
- central_directory.Initialize(directory_map->data(), 0 /*offset*/, cd_size);
- } else {
- if (mapped_zip.GetBasePtr() == nullptr) {
- ALOGE("Zip: Failed to map central directory, bad mapped_zip base pointer");
- return false;
- }
- if (static_cast<off64_t>(cd_start_offset) + static_cast<off64_t>(cd_size) >
- mapped_zip.GetFileLength()) {
- ALOGE(
- "Zip: Failed to map central directory, offset exceeds mapped memory region ("
- "start_offset %" PRId64 ", cd_size %zu, mapped_region_size %" PRId64 ")",
- static_cast<int64_t>(cd_start_offset), cd_size, mapped_zip.GetFileLength());
- return false;
- }
-
- central_directory.Initialize(mapped_zip.GetBasePtr(), cd_start_offset, cd_size);
- }
- return true;
-}
-
-tm ZipEntry::GetModificationTime() const {
- tm t = {};
-
- t.tm_hour = (mod_time >> 11) & 0x1f;
- t.tm_min = (mod_time >> 5) & 0x3f;
- t.tm_sec = (mod_time & 0x1f) << 1;
-
- t.tm_year = ((mod_time >> 25) & 0x7f) + 80;
- t.tm_mon = ((mod_time >> 21) & 0xf) - 1;
- t.tm_mday = (mod_time >> 16) & 0x1f;
-
- return t;
-}
diff --git a/libziparchive/zip_archive_benchmark.cpp b/libziparchive/zip_archive_benchmark.cpp
deleted file mode 100644
index 09d3b8a..0000000
--- a/libziparchive/zip_archive_benchmark.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * 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.
- */
-
-#include <cstdio>
-#include <cstdlib>
-#include <cstring>
-#include <iostream>
-#include <string>
-#include <tuple>
-#include <vector>
-
-#include <android-base/test_utils.h>
-#include <benchmark/benchmark.h>
-#include <ziparchive/zip_archive.h>
-#include <ziparchive/zip_archive_stream_entry.h>
-#include <ziparchive/zip_writer.h>
-
-static TemporaryFile* CreateZip() {
- TemporaryFile* result = new TemporaryFile;
- FILE* fp = fdopen(result->fd, "w");
-
- ZipWriter writer(fp);
- std::string lastName = "file";
- for (size_t i = 0; i < 1000; i++) {
- // Make file names longer and longer.
- lastName = lastName + std::to_string(i);
- writer.StartEntry(lastName.c_str(), ZipWriter::kCompress);
- writer.WriteBytes("helo", 4);
- writer.FinishEntry();
- }
- writer.Finish();
- fclose(fp);
-
- return result;
-}
-
-static void FindEntry_no_match(benchmark::State& state) {
- // Create a temporary zip archive.
- std::unique_ptr<TemporaryFile> temp_file(CreateZip());
- ZipArchiveHandle handle;
- ZipEntry data;
-
- // In order to walk through all file names in the archive, look for a name
- // that does not exist in the archive.
- std::string_view name("thisFileNameDoesNotExist");
-
- // Start the benchmark.
- for (auto _ : state) {
- OpenArchive(temp_file->path, &handle);
- FindEntry(handle, name, &data);
- CloseArchive(handle);
- }
-}
-BENCHMARK(FindEntry_no_match);
-
-static void Iterate_all_files(benchmark::State& state) {
- std::unique_ptr<TemporaryFile> temp_file(CreateZip());
- ZipArchiveHandle handle;
- void* iteration_cookie;
- ZipEntry data;
- std::string name;
-
- for (auto _ : state) {
- OpenArchive(temp_file->path, &handle);
- StartIteration(handle, &iteration_cookie);
- while (Next(iteration_cookie, &data, &name) == 0) {
- }
- EndIteration(iteration_cookie);
- CloseArchive(handle);
- }
-}
-BENCHMARK(Iterate_all_files);
-
-static void StartAlignedEntry(benchmark::State& state) {
- TemporaryFile file;
- FILE* fp = fdopen(file.fd, "w");
-
- ZipWriter writer(fp);
-
- auto alignment = uint32_t(state.range(0));
- std::string name = "name";
- int counter = 0;
- for (auto _ : state) {
- writer.StartAlignedEntry(name + std::to_string(counter++), 0, alignment);
- state.PauseTiming();
- writer.WriteBytes("hola", 4);
- writer.FinishEntry();
- state.ResumeTiming();
- }
-
- writer.Finish();
- fclose(fp);
-}
-BENCHMARK(StartAlignedEntry)->Arg(2)->Arg(16)->Arg(1024)->Arg(4096);
-
-
-BENCHMARK_MAIN();
diff --git a/libziparchive/zip_archive_common.h b/libziparchive/zip_archive_common.h
deleted file mode 100644
index 8b99bde..0000000
--- a/libziparchive/zip_archive_common.h
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef LIBZIPARCHIVE_ZIPARCHIVECOMMON_H_
-#define LIBZIPARCHIVE_ZIPARCHIVECOMMON_H_
-
-#include "android-base/macros.h"
-
-#include <inttypes.h>
-
-// The "end of central directory" (EOCD) record. Each archive
-// contains exactly once such record which appears at the end of
-// the archive. It contains archive wide information like the
-// number of entries in the archive and the offset to the central
-// directory of the offset.
-struct EocdRecord {
- static const uint32_t kSignature = 0x06054b50;
-
- // End of central directory signature, should always be
- // |kSignature|.
- uint32_t eocd_signature;
- // The number of the current "disk", i.e, the "disk" that this
- // central directory is on.
- //
- // This implementation assumes that each archive spans a single
- // disk only. i.e, that disk_num == 1.
- uint16_t disk_num;
- // The disk where the central directory starts.
- //
- // This implementation assumes that each archive spans a single
- // disk only. i.e, that cd_start_disk == 1.
- uint16_t cd_start_disk;
- // The number of central directory records on this disk.
- //
- // This implementation assumes that each archive spans a single
- // disk only. i.e, that num_records_on_disk == num_records.
- uint16_t num_records_on_disk;
- // The total number of central directory records.
- uint16_t num_records;
- // The size of the central directory (in bytes).
- uint32_t cd_size;
- // The offset of the start of the central directory, relative
- // to the start of the file.
- uint32_t cd_start_offset;
- // Length of the central directory comment.
- uint16_t comment_length;
-
- private:
- EocdRecord() = default;
- DISALLOW_COPY_AND_ASSIGN(EocdRecord);
-} __attribute__((packed));
-
-// A structure representing the fixed length fields for a single
-// record in the central directory of the archive. In addition to
-// the fixed length fields listed here, each central directory
-// record contains a variable length "file_name" and "extra_field"
-// whose lengths are given by |file_name_length| and |extra_field_length|
-// respectively.
-struct CentralDirectoryRecord {
- static const uint32_t kSignature = 0x02014b50;
-
- // The start of record signature. Must be |kSignature|.
- uint32_t record_signature;
- // Source tool version. Top byte gives source OS.
- uint16_t version_made_by;
- // Tool version. Ignored by this implementation.
- uint16_t version_needed;
- // The "general purpose bit flags" for this entry. The only
- // flag value that we currently check for is the "data descriptor"
- // flag.
- uint16_t gpb_flags;
- // The compression method for this entry, one of |kCompressStored|
- // and |kCompressDeflated|.
- uint16_t compression_method;
- // The file modification time and date for this entry.
- uint16_t last_mod_time;
- uint16_t last_mod_date;
- // The CRC-32 checksum for this entry.
- uint32_t crc32;
- // The compressed size (in bytes) of this entry.
- uint32_t compressed_size;
- // The uncompressed size (in bytes) of this entry.
- uint32_t uncompressed_size;
- // The length of the entry file name in bytes. The file name
- // will appear immediately after this record.
- uint16_t file_name_length;
- // The length of the extra field info (in bytes). This data
- // will appear immediately after the entry file name.
- uint16_t extra_field_length;
- // The length of the entry comment (in bytes). This data will
- // appear immediately after the extra field.
- uint16_t comment_length;
- // The start disk for this entry. Ignored by this implementation).
- uint16_t file_start_disk;
- // File attributes. Ignored by this implementation.
- uint16_t internal_file_attributes;
- // File attributes. For archives created on Unix, the top bits are the mode.
- uint32_t external_file_attributes;
- // The offset to the local file header for this entry, from the
- // beginning of this archive.
- uint32_t local_file_header_offset;
-
- private:
- CentralDirectoryRecord() = default;
- DISALLOW_COPY_AND_ASSIGN(CentralDirectoryRecord);
-} __attribute__((packed));
-
-// The local file header for a given entry. This duplicates information
-// present in the central directory of the archive. It is an error for
-// the information here to be different from the central directory
-// information for a given entry.
-struct LocalFileHeader {
- static const uint32_t kSignature = 0x04034b50;
-
- // The local file header signature, must be |kSignature|.
- uint32_t lfh_signature;
- // Tool version. Ignored by this implementation.
- uint16_t version_needed;
- // The "general purpose bit flags" for this entry. The only
- // flag value that we currently check for is the "data descriptor"
- // flag.
- uint16_t gpb_flags;
- // The compression method for this entry, one of |kCompressStored|
- // and |kCompressDeflated|.
- uint16_t compression_method;
- // The file modification time and date for this entry.
- uint16_t last_mod_time;
- uint16_t last_mod_date;
- // The CRC-32 checksum for this entry.
- uint32_t crc32;
- // The compressed size (in bytes) of this entry.
- uint32_t compressed_size;
- // The uncompressed size (in bytes) of this entry.
- uint32_t uncompressed_size;
- // The length of the entry file name in bytes. The file name
- // will appear immediately after this record.
- uint16_t file_name_length;
- // The length of the extra field info (in bytes). This data
- // will appear immediately after the entry file name.
- uint16_t extra_field_length;
-
- private:
- LocalFileHeader() = default;
- DISALLOW_COPY_AND_ASSIGN(LocalFileHeader);
-} __attribute__((packed));
-
-struct DataDescriptor {
- // The *optional* data descriptor start signature.
- static const uint32_t kOptSignature = 0x08074b50;
-
- // CRC-32 checksum of the entry.
- uint32_t crc32;
- // Compressed size of the entry.
- uint32_t compressed_size;
- // Uncompressed size of the entry.
- uint32_t uncompressed_size;
-
- private:
- DataDescriptor() = default;
- DISALLOW_COPY_AND_ASSIGN(DataDescriptor);
-} __attribute__((packed));
-
-// mask value that signifies that the entry has a DD
-static const uint32_t kGPBDDFlagMask = 0x0008;
-
-// The maximum size of a central directory or a file
-// comment in bytes.
-static const uint32_t kMaxCommentLen = 65535;
-
-#endif /* LIBZIPARCHIVE_ZIPARCHIVECOMMON_H_ */
diff --git a/libziparchive/zip_archive_private.h b/libziparchive/zip_archive_private.h
deleted file mode 100644
index 1d05fc7..0000000
--- a/libziparchive/zip_archive_private.h
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * 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.
- */
-
-#pragma once
-
-#include <ziparchive/zip_archive.h>
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include <memory>
-#include <vector>
-
-#include "android-base/macros.h"
-#include "android-base/mapped_file.h"
-
-static const char* kErrorMessages[] = {
- "Success",
- "Iteration ended",
- "Zlib error",
- "Invalid file",
- "Invalid handle",
- "Duplicate entries in archive",
- "Empty archive",
- "Entry not found",
- "Invalid offset",
- "Inconsistent information",
- "Invalid entry name",
- "I/O error",
- "File mapping failed",
- "Allocation failed",
-};
-
-enum ErrorCodes : int32_t {
- kIterationEnd = -1,
-
- // We encountered a Zlib error when inflating a stream from this file.
- // Usually indicates file corruption.
- kZlibError = -2,
-
- // The input file cannot be processed as a zip archive. Usually because
- // it's too small, too large or does not have a valid signature.
- kInvalidFile = -3,
-
- // An invalid iteration / ziparchive handle was passed in as an input
- // argument.
- kInvalidHandle = -4,
-
- // The zip archive contained two (or possibly more) entries with the same
- // name.
- kDuplicateEntry = -5,
-
- // The zip archive contains no entries.
- kEmptyArchive = -6,
-
- // The specified entry was not found in the archive.
- kEntryNotFound = -7,
-
- // The zip archive contained an invalid local file header pointer.
- kInvalidOffset = -8,
-
- // The zip archive contained inconsistent entry information. This could
- // be because the central directory & local file header did not agree, or
- // if the actual uncompressed length or crc32 do not match their declared
- // values.
- kInconsistentInformation = -9,
-
- // An invalid entry name was encountered.
- kInvalidEntryName = -10,
-
- // An I/O related system call (read, lseek, ftruncate, map) failed.
- kIoError = -11,
-
- // We were not able to mmap the central directory or entry contents.
- kMmapFailed = -12,
-
- // An allocation failed.
- kAllocationFailed = -13,
-
- kLastErrorCode = kAllocationFailed,
-};
-
-class MappedZipFile {
- public:
- explicit MappedZipFile(const int fd)
- : has_fd_(true), fd_(fd), base_ptr_(nullptr), data_length_(0) {}
-
- explicit MappedZipFile(const void* address, size_t length)
- : has_fd_(false), fd_(-1), base_ptr_(address), data_length_(static_cast<off64_t>(length)) {}
-
- bool HasFd() const { return has_fd_; }
-
- int GetFileDescriptor() const;
-
- const void* GetBasePtr() const;
-
- off64_t GetFileLength() const;
-
- bool ReadAtOffset(uint8_t* buf, size_t len, off64_t off) const;
-
- private:
- // If has_fd_ is true, fd is valid and we'll read contents of a zip archive
- // from the file. Otherwise, we're opening the archive from a memory mapped
- // file. In that case, base_ptr_ points to the start of the memory region and
- // data_length_ defines the file length.
- const bool has_fd_;
-
- const int fd_;
-
- const void* const base_ptr_;
- const off64_t data_length_;
-};
-
-class CentralDirectory {
- public:
- CentralDirectory(void) : base_ptr_(nullptr), length_(0) {}
-
- const uint8_t* GetBasePtr() const { return base_ptr_; }
-
- size_t GetMapLength() const { return length_; }
-
- void Initialize(const void* map_base_ptr, off64_t cd_start_offset, size_t cd_size);
-
- private:
- const uint8_t* base_ptr_;
- size_t length_;
-};
-
-/**
- * More space efficient string representation of strings in an mmaped zipped
- * file than std::string_view. Using std::string_view as an entry in the
- * ZipArchive hash table wastes space. std::string_view stores a pointer to a
- * string (on 64 bit, 8 bytes) and the length to read from that pointer,
- * 2 bytes. Because of alignment, the structure consumes 16 bytes, wasting
- * 6 bytes.
- *
- * ZipStringOffset stores a 4 byte offset from a fixed location in the memory
- * mapped file instead of the entire address, consuming 8 bytes with alignment.
- */
-struct ZipStringOffset {
- uint32_t name_offset;
- uint16_t name_length;
-
- const std::string_view ToStringView(const uint8_t* start) const {
- return std::string_view{reinterpret_cast<const char*>(start + name_offset), name_length};
- }
-};
-
-struct ZipArchive {
- // open Zip archive
- mutable MappedZipFile mapped_zip;
- const bool close_file;
-
- // mapped central directory area
- off64_t directory_offset;
- CentralDirectory central_directory;
- std::unique_ptr<android::base::MappedFile> directory_map;
-
- // number of entries in the Zip archive
- uint16_t num_entries;
-
- // We know how many entries are in the Zip archive, so we can have a
- // fixed-size hash table. We define a load factor of 0.75 and over
- // allocate so the maximum number entries can never be higher than
- // ((4 * UINT16_MAX) / 3 + 1) which can safely fit into a uint32_t.
- uint32_t hash_table_size;
- ZipStringOffset* hash_table;
-
- ZipArchive(const int fd, bool assume_ownership);
- ZipArchive(const void* address, size_t length);
- ~ZipArchive();
-
- bool InitializeCentralDirectory(off64_t cd_start_offset, size_t cd_size);
-};
diff --git a/libziparchive/zip_archive_stream_entry.cc b/libziparchive/zip_archive_stream_entry.cc
deleted file mode 100644
index 1ec95b6..0000000
--- a/libziparchive/zip_archive_stream_entry.cc
+++ /dev/null
@@ -1,323 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "ZIPARCHIVE"
-
-// Read-only stream access to Zip Archive entries.
-#include <errno.h>
-#include <inttypes.h>
-#include <string.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <memory>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <log/log.h>
-
-#include <ziparchive/zip_archive.h>
-#include <ziparchive/zip_archive_stream_entry.h>
-#include <zlib.h>
-
-#include "zip_archive_private.h"
-
-static constexpr size_t kBufSize = 65535;
-
-bool ZipArchiveStreamEntry::Init(const ZipEntry& entry) {
- crc32_ = entry.crc32;
- offset_ = entry.offset;
- return true;
-}
-
-class ZipArchiveStreamEntryUncompressed : public ZipArchiveStreamEntry {
- public:
- explicit ZipArchiveStreamEntryUncompressed(ZipArchiveHandle handle)
- : ZipArchiveStreamEntry(handle) {}
- virtual ~ZipArchiveStreamEntryUncompressed() {}
-
- const std::vector<uint8_t>* Read() override;
-
- bool Verify() override;
-
- protected:
- bool Init(const ZipEntry& entry) override;
-
- uint32_t length_ = 0u;
-
- private:
- std::vector<uint8_t> data_;
- uint32_t computed_crc32_ = 0u;
-};
-
-bool ZipArchiveStreamEntryUncompressed::Init(const ZipEntry& entry) {
- if (!ZipArchiveStreamEntry::Init(entry)) {
- return false;
- }
-
- length_ = entry.uncompressed_length;
-
- data_.resize(kBufSize);
- computed_crc32_ = 0;
-
- return true;
-}
-
-const std::vector<uint8_t>* ZipArchiveStreamEntryUncompressed::Read() {
- // Simple sanity check. The vector should *only* be handled by this code. A caller
- // should not const-cast and modify the capacity. This may invalidate next_out.
- //
- // Note: it would be better to store the results of data() across Read calls.
- CHECK_EQ(data_.capacity(), kBufSize);
-
- if (length_ == 0) {
- return nullptr;
- }
-
- size_t bytes = (length_ > data_.size()) ? data_.size() : length_;
- ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
- errno = 0;
- if (!archive->mapped_zip.ReadAtOffset(data_.data(), bytes, offset_)) {
- if (errno != 0) {
- ALOGE("Error reading from archive fd: %s", strerror(errno));
- } else {
- ALOGE("Short read of zip file, possibly corrupted zip?");
- }
- length_ = 0;
- return nullptr;
- }
-
- if (bytes < data_.size()) {
- data_.resize(bytes);
- }
- computed_crc32_ = static_cast<uint32_t>(
- crc32(computed_crc32_, data_.data(), static_cast<uint32_t>(data_.size())));
- length_ -= bytes;
- offset_ += bytes;
- return &data_;
-}
-
-bool ZipArchiveStreamEntryUncompressed::Verify() {
- return length_ == 0 && crc32_ == computed_crc32_;
-}
-
-class ZipArchiveStreamEntryCompressed : public ZipArchiveStreamEntry {
- public:
- explicit ZipArchiveStreamEntryCompressed(ZipArchiveHandle handle)
- : ZipArchiveStreamEntry(handle) {}
- virtual ~ZipArchiveStreamEntryCompressed();
-
- const std::vector<uint8_t>* Read() override;
-
- bool Verify() override;
-
- protected:
- bool Init(const ZipEntry& entry) override;
-
- private:
- bool z_stream_init_ = false;
- z_stream z_stream_;
- std::vector<uint8_t> in_;
- std::vector<uint8_t> out_;
- uint32_t uncompressed_length_ = 0u;
- uint32_t compressed_length_ = 0u;
- uint32_t computed_crc32_ = 0u;
-};
-
-// This method is using libz macros with old-style-casts
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wold-style-cast"
-static inline int zlib_inflateInit2(z_stream* stream, int window_bits) {
- return inflateInit2(stream, window_bits);
-}
-#pragma GCC diagnostic pop
-
-bool ZipArchiveStreamEntryCompressed::Init(const ZipEntry& entry) {
- if (!ZipArchiveStreamEntry::Init(entry)) {
- return false;
- }
-
- // Initialize the zlib stream struct.
- memset(&z_stream_, 0, sizeof(z_stream_));
- z_stream_.zalloc = Z_NULL;
- z_stream_.zfree = Z_NULL;
- z_stream_.opaque = Z_NULL;
- z_stream_.next_in = nullptr;
- z_stream_.avail_in = 0;
- z_stream_.avail_out = 0;
- z_stream_.data_type = Z_UNKNOWN;
-
- // Use the undocumented "negative window bits" feature to tell zlib
- // that there's no zlib header waiting for it.
- int zerr = zlib_inflateInit2(&z_stream_, -MAX_WBITS);
- if (zerr != Z_OK) {
- if (zerr == Z_VERSION_ERROR) {
- ALOGE("Installed zlib is not compatible with linked version (%s)", ZLIB_VERSION);
- } else {
- ALOGE("Call to inflateInit2 failed (zerr=%d)", zerr);
- }
-
- return false;
- }
-
- z_stream_init_ = true;
-
- uncompressed_length_ = entry.uncompressed_length;
- compressed_length_ = entry.compressed_length;
-
- out_.resize(kBufSize);
- in_.resize(kBufSize);
-
- computed_crc32_ = 0;
-
- return true;
-}
-
-ZipArchiveStreamEntryCompressed::~ZipArchiveStreamEntryCompressed() {
- if (z_stream_init_) {
- inflateEnd(&z_stream_);
- z_stream_init_ = false;
- }
-}
-
-bool ZipArchiveStreamEntryCompressed::Verify() {
- return z_stream_init_ && uncompressed_length_ == 0 && compressed_length_ == 0 &&
- crc32_ == computed_crc32_;
-}
-
-const std::vector<uint8_t>* ZipArchiveStreamEntryCompressed::Read() {
- // Simple sanity check. The vector should *only* be handled by this code. A caller
- // should not const-cast and modify the capacity. This may invalidate next_out.
- //
- // Note: it would be better to store the results of data() across Read calls.
- CHECK_EQ(out_.capacity(), kBufSize);
-
- if (z_stream_.avail_out == 0) {
- z_stream_.next_out = out_.data();
- z_stream_.avail_out = static_cast<uint32_t>(out_.size());
- ;
- }
-
- while (true) {
- if (z_stream_.avail_in == 0) {
- if (compressed_length_ == 0) {
- return nullptr;
- }
- DCHECK_LE(in_.size(), std::numeric_limits<uint32_t>::max()); // Should be buf size = 64k.
- uint32_t bytes = (compressed_length_ > in_.size()) ? static_cast<uint32_t>(in_.size())
- : compressed_length_;
- ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
- errno = 0;
- if (!archive->mapped_zip.ReadAtOffset(in_.data(), bytes, offset_)) {
- if (errno != 0) {
- ALOGE("Error reading from archive fd: %s", strerror(errno));
- } else {
- ALOGE("Short read of zip file, possibly corrupted zip?");
- }
- return nullptr;
- }
-
- compressed_length_ -= bytes;
- offset_ += bytes;
- z_stream_.next_in = in_.data();
- z_stream_.avail_in = bytes;
- }
-
- int zerr = inflate(&z_stream_, Z_NO_FLUSH);
- if (zerr != Z_OK && zerr != Z_STREAM_END) {
- ALOGE("inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)", zerr, z_stream_.next_in,
- z_stream_.avail_in, z_stream_.next_out, z_stream_.avail_out);
- return nullptr;
- }
-
- if (z_stream_.avail_out == 0) {
- uncompressed_length_ -= out_.size();
- computed_crc32_ = static_cast<uint32_t>(
- crc32(computed_crc32_, out_.data(), static_cast<uint32_t>(out_.size())));
- return &out_;
- }
- if (zerr == Z_STREAM_END) {
- if (z_stream_.avail_out != 0) {
- // Resize the vector down to the actual size of the data.
- out_.resize(out_.size() - z_stream_.avail_out);
- computed_crc32_ = static_cast<uint32_t>(
- crc32(computed_crc32_, out_.data(), static_cast<uint32_t>(out_.size())));
- uncompressed_length_ -= out_.size();
- return &out_;
- }
- return nullptr;
- }
- }
- return nullptr;
-}
-
-class ZipArchiveStreamEntryRawCompressed : public ZipArchiveStreamEntryUncompressed {
- public:
- explicit ZipArchiveStreamEntryRawCompressed(ZipArchiveHandle handle)
- : ZipArchiveStreamEntryUncompressed(handle) {}
- virtual ~ZipArchiveStreamEntryRawCompressed() {}
-
- bool Verify() override;
-
- protected:
- bool Init(const ZipEntry& entry) override;
-};
-
-bool ZipArchiveStreamEntryRawCompressed::Init(const ZipEntry& entry) {
- if (!ZipArchiveStreamEntryUncompressed::Init(entry)) {
- return false;
- }
- length_ = entry.compressed_length;
-
- return true;
-}
-
-bool ZipArchiveStreamEntryRawCompressed::Verify() {
- return length_ == 0;
-}
-
-ZipArchiveStreamEntry* ZipArchiveStreamEntry::Create(ZipArchiveHandle handle,
- const ZipEntry& entry) {
- ZipArchiveStreamEntry* stream = nullptr;
- if (entry.method != kCompressStored) {
- stream = new ZipArchiveStreamEntryCompressed(handle);
- } else {
- stream = new ZipArchiveStreamEntryUncompressed(handle);
- }
- if (stream && !stream->Init(entry)) {
- delete stream;
- stream = nullptr;
- }
-
- return stream;
-}
-
-ZipArchiveStreamEntry* ZipArchiveStreamEntry::CreateRaw(ZipArchiveHandle handle,
- const ZipEntry& entry) {
- ZipArchiveStreamEntry* stream = nullptr;
- if (entry.method == kCompressStored) {
- // Not compressed, don't need to do anything special.
- stream = new ZipArchiveStreamEntryUncompressed(handle);
- } else {
- stream = new ZipArchiveStreamEntryRawCompressed(handle);
- }
- if (stream && !stream->Init(entry)) {
- delete stream;
- stream = nullptr;
- }
- return stream;
-}
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
deleted file mode 100644
index 0916304..0000000
--- a/libziparchive/zip_archive_test.cc
+++ /dev/null
@@ -1,791 +0,0 @@
-/*
- * Copyright (C) 2013 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 "zip_archive_private.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <getopt.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <memory>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/mapped_file.h>
-#include <android-base/unique_fd.h>
-#include <gtest/gtest.h>
-#include <ziparchive/zip_archive.h>
-#include <ziparchive/zip_archive_stream_entry.h>
-
-static std::string test_data_dir = android::base::GetExecutableDirectory() + "/testdata";
-
-static const std::string kValidZip = "valid.zip";
-static const std::string kLargeZip = "large.zip";
-static const std::string kBadCrcZip = "bad_crc.zip";
-
-static const std::vector<uint8_t> kATxtContents{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'a',
- 'b', 'c', 'd', 'e', 'f', 'g', 'h', '\n'};
-
-static const std::vector<uint8_t> kATxtContentsCompressed{'K', 'L', 'J', 'N', 'I', 'M', 'K',
- 207, 'H', 132, 210, '\\', '\0'};
-
-static const std::vector<uint8_t> kBTxtContents{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', '\n'};
-
-static int32_t OpenArchiveWrapper(const std::string& name, ZipArchiveHandle* handle) {
- const std::string abs_path = test_data_dir + "/" + name;
- return OpenArchive(abs_path.c_str(), handle);
-}
-
-TEST(ziparchive, Open) {
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
- CloseArchive(handle);
-
- ASSERT_EQ(kInvalidEntryName, OpenArchiveWrapper("bad_filename.zip", &handle));
- CloseArchive(handle);
-}
-
-TEST(ziparchive, OutOfBound) {
- ZipArchiveHandle handle;
- ASSERT_EQ(kInvalidOffset, OpenArchiveWrapper("crash.apk", &handle));
- CloseArchive(handle);
-}
-
-TEST(ziparchive, EmptyArchive) {
- ZipArchiveHandle handle;
- ASSERT_EQ(kEmptyArchive, OpenArchiveWrapper("empty.zip", &handle));
- CloseArchive(handle);
-}
-
-TEST(ziparchive, ZeroSizeCentralDirectory) {
- ZipArchiveHandle handle;
- ASSERT_EQ(kInvalidFile, OpenArchiveWrapper("zero-size-cd.zip", &handle));
- CloseArchive(handle);
-}
-
-TEST(ziparchive, OpenMissing) {
- ZipArchiveHandle handle;
- ASSERT_NE(0, OpenArchiveWrapper("missing.zip", &handle));
-
- // Confirm the file descriptor is not going to be mistaken for a valid one.
- ASSERT_EQ(-1, GetFileDescriptor(handle));
-}
-
-TEST(ziparchive, OpenAssumeFdOwnership) {
- int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY | O_BINARY);
- ASSERT_NE(-1, fd);
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFd(fd, "OpenWithAssumeFdOwnership", &handle));
- CloseArchive(handle);
- ASSERT_EQ(-1, lseek(fd, 0, SEEK_SET));
- ASSERT_EQ(EBADF, errno);
-}
-
-TEST(ziparchive, OpenDoNotAssumeFdOwnership) {
- int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY | O_BINARY);
- ASSERT_NE(-1, fd);
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFd(fd, "OpenWithAssumeFdOwnership", &handle, false));
- CloseArchive(handle);
- ASSERT_EQ(0, lseek(fd, 0, SEEK_SET));
- close(fd);
-}
-
-TEST(ziparchive, Iteration_std_string_view) {
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
-
- void* iteration_cookie;
- ASSERT_EQ(0, StartIteration(handle, &iteration_cookie));
-
- ZipEntry data;
- std::vector<std::string_view> names;
- std::string_view name;
- while (Next(iteration_cookie, &data, &name) == 0) names.push_back(name);
-
- // Assert that the names are as expected.
- std::vector<std::string_view> expected_names{"a.txt", "b.txt", "b/", "b/c.txt", "b/d.txt"};
- std::sort(names.begin(), names.end());
- ASSERT_EQ(expected_names, names);
-
- CloseArchive(handle);
-}
-
-static void AssertIterationOrder(const std::string_view prefix, const std::string_view suffix,
- const std::vector<std::string>& expected_names_sorted) {
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
-
- void* iteration_cookie;
- ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, prefix, suffix));
-
- ZipEntry data;
- std::vector<std::string> names;
-
- std::string name;
- for (size_t i = 0; i < expected_names_sorted.size(); ++i) {
- ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
- names.push_back(name);
- }
-
- // End of iteration.
- ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
- CloseArchive(handle);
-
- // Assert that the names are as expected.
- std::sort(names.begin(), names.end());
- ASSERT_EQ(expected_names_sorted, names);
-}
-
-TEST(ziparchive, Iteration) {
- static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt", "b.txt", "b/", "b/c.txt",
- "b/d.txt"};
-
- AssertIterationOrder("", "", kExpectedMatchesSorted);
-}
-
-TEST(ziparchive, IterationWithPrefix) {
- static const std::vector<std::string> kExpectedMatchesSorted = {"b/", "b/c.txt", "b/d.txt"};
-
- AssertIterationOrder("b/", "", kExpectedMatchesSorted);
-}
-
-TEST(ziparchive, IterationWithSuffix) {
- static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt", "b.txt", "b/c.txt",
- "b/d.txt"};
-
- AssertIterationOrder("", ".txt", kExpectedMatchesSorted);
-}
-
-TEST(ziparchive, IterationWithPrefixAndSuffix) {
- static const std::vector<std::string> kExpectedMatchesSorted = {"b.txt", "b/c.txt", "b/d.txt"};
-
- AssertIterationOrder("b", ".txt", kExpectedMatchesSorted);
-}
-
-TEST(ziparchive, IterationWithBadPrefixAndSuffix) {
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
-
- void* iteration_cookie;
- ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, "x", "y"));
-
- ZipEntry data;
- std::string name;
-
- // End of iteration.
- ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
-
- CloseArchive(handle);
-}
-
-TEST(ziparchive, FindEntry) {
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
-
- ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, "a.txt", &data));
-
- // Known facts about a.txt, from zipinfo -v.
- ASSERT_EQ(63, data.offset);
- ASSERT_EQ(kCompressDeflated, data.method);
- ASSERT_EQ(static_cast<uint32_t>(17), data.uncompressed_length);
- ASSERT_EQ(static_cast<uint32_t>(13), data.compressed_length);
- ASSERT_EQ(0x950821c5, data.crc32);
- ASSERT_EQ(static_cast<uint32_t>(0x438a8005), data.mod_time);
-
- // An entry that doesn't exist. Should be a negative return code.
- ASSERT_LT(FindEntry(handle, "this file does not exist", &data), 0);
-
- CloseArchive(handle);
-}
-
-TEST(ziparchive, FindEntry_empty) {
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
-
- ZipEntry data;
- ASSERT_EQ(kInvalidEntryName, FindEntry(handle, "", &data));
-
- CloseArchive(handle);
-}
-
-TEST(ziparchive, FindEntry_too_long) {
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
-
- std::string very_long_name(65536, 'x');
- ZipEntry data;
- ASSERT_EQ(kInvalidEntryName, FindEntry(handle, very_long_name, &data));
-
- CloseArchive(handle);
-}
-
-TEST(ziparchive, TestInvalidDeclaredLength) {
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveWrapper("declaredlength.zip", &handle));
-
- void* iteration_cookie;
- ASSERT_EQ(0, StartIteration(handle, &iteration_cookie));
-
- std::string name;
- ZipEntry data;
-
- ASSERT_EQ(Next(iteration_cookie, &data, &name), 0);
- ASSERT_EQ(Next(iteration_cookie, &data, &name), 0);
-
- CloseArchive(handle);
-}
-
-TEST(ziparchive, ExtractToMemory) {
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
-
- // An entry that's deflated.
- ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, "a.txt", &data));
- const uint32_t a_size = data.uncompressed_length;
- ASSERT_EQ(a_size, kATxtContents.size());
- uint8_t* buffer = new uint8_t[a_size];
- ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, a_size));
- ASSERT_EQ(0, memcmp(buffer, kATxtContents.data(), a_size));
- delete[] buffer;
-
- // An entry that's stored.
- ASSERT_EQ(0, FindEntry(handle, "b.txt", &data));
- const uint32_t b_size = data.uncompressed_length;
- ASSERT_EQ(b_size, kBTxtContents.size());
- buffer = new uint8_t[b_size];
- ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, b_size));
- ASSERT_EQ(0, memcmp(buffer, kBTxtContents.data(), b_size));
- delete[] buffer;
-
- CloseArchive(handle);
-}
-
-static const uint32_t kEmptyEntriesZip[] = {
- 0x04034b50, 0x0000000a, 0x63600000, 0x00004438, 0x00000000, 0x00000000, 0x00090000,
- 0x6d65001c, 0x2e797470, 0x55747874, 0x03000954, 0x52e25c13, 0x52e25c24, 0x000b7875,
- 0x42890401, 0x88040000, 0x50000013, 0x1e02014b, 0x00000a03, 0x60000000, 0x00443863,
- 0x00000000, 0x00000000, 0x09000000, 0x00001800, 0x00000000, 0xa0000000, 0x00000081,
- 0x706d6500, 0x742e7974, 0x54557478, 0x13030005, 0x7552e25c, 0x01000b78, 0x00428904,
- 0x13880400, 0x4b500000, 0x00000605, 0x00010000, 0x004f0001, 0x00430000, 0x00000000};
-
-// This is a zip file containing a single entry (ab.txt) that contains
-// 90072 repetitions of the string "ab\n" and has an uncompressed length
-// of 270216 bytes.
-static const uint16_t kAbZip[] = {
- 0x4b50, 0x0403, 0x0014, 0x0000, 0x0008, 0x51d2, 0x4698, 0xc4b0, 0x2cda, 0x011b, 0x0000, 0x1f88,
- 0x0004, 0x0006, 0x001c, 0x6261, 0x742e, 0x7478, 0x5455, 0x0009, 0x7c03, 0x3a09, 0x7c55, 0x3a09,
- 0x7555, 0x0b78, 0x0100, 0x8904, 0x0042, 0x0400, 0x1388, 0x0000, 0xc2ed, 0x0d31, 0x0000, 0x030c,
- 0x7fa0, 0x3b2e, 0x22ff, 0xa2aa, 0x841f, 0x45fc, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0xdd55, 0x502c, 0x014b, 0x1e02, 0x1403, 0x0000, 0x0800, 0xd200,
- 0x9851, 0xb046, 0xdac4, 0x1b2c, 0x0001, 0x8800, 0x041f, 0x0600, 0x1800, 0x0000, 0x0000, 0x0100,
- 0x0000, 0xa000, 0x0081, 0x0000, 0x6100, 0x2e62, 0x7874, 0x5574, 0x0554, 0x0300, 0x097c, 0x553a,
- 0x7875, 0x000b, 0x0401, 0x4289, 0x0000, 0x8804, 0x0013, 0x5000, 0x054b, 0x0006, 0x0000, 0x0100,
- 0x0100, 0x4c00, 0x0000, 0x5b00, 0x0001, 0x0000, 0x0000};
-
-static const std::string kAbTxtName("ab.txt");
-static const size_t kAbUncompressedSize = 270216;
-
-TEST(ziparchive, EmptyEntries) {
- TemporaryFile tmp_file;
- ASSERT_NE(-1, tmp_file.fd);
- ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, kEmptyEntriesZip, sizeof(kEmptyEntriesZip)));
-
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle, false));
-
- ZipEntry entry;
- ASSERT_EQ(0, FindEntry(handle, "empty.txt", &entry));
- ASSERT_EQ(static_cast<uint32_t>(0), entry.uncompressed_length);
- uint8_t buffer[1];
- ASSERT_EQ(0, ExtractToMemory(handle, &entry, buffer, 1));
-
- TemporaryFile tmp_output_file;
- ASSERT_NE(-1, tmp_output_file.fd);
- ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_output_file.fd));
-
- struct stat stat_buf;
- ASSERT_EQ(0, fstat(tmp_output_file.fd, &stat_buf));
- ASSERT_EQ(0, stat_buf.st_size);
-}
-
-TEST(ziparchive, EntryLargerThan32K) {
- TemporaryFile tmp_file;
- ASSERT_NE(-1, tmp_file.fd);
- ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, reinterpret_cast<const uint8_t*>(kAbZip),
- sizeof(kAbZip) - 1));
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EntryLargerThan32KTest", &handle, false));
-
- ZipEntry entry;
- ASSERT_EQ(0, FindEntry(handle, kAbTxtName, &entry));
- ASSERT_EQ(kAbUncompressedSize, entry.uncompressed_length);
-
- // Extract the entry to memory.
- std::vector<uint8_t> buffer(kAbUncompressedSize);
- ASSERT_EQ(0, ExtractToMemory(handle, &entry, &buffer[0], static_cast<uint32_t>(buffer.size())));
-
- // Extract the entry to a file.
- TemporaryFile tmp_output_file;
- ASSERT_NE(-1, tmp_output_file.fd);
- ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_output_file.fd));
-
- // Make sure the extracted file size is as expected.
- struct stat stat_buf;
- ASSERT_EQ(0, fstat(tmp_output_file.fd, &stat_buf));
- ASSERT_EQ(kAbUncompressedSize, static_cast<size_t>(stat_buf.st_size));
-
- // Read the file back to a buffer and make sure the contents are
- // the same as the memory buffer we extracted directly to.
- std::vector<uint8_t> file_contents(kAbUncompressedSize);
- ASSERT_EQ(0, lseek(tmp_output_file.fd, 0, SEEK_SET));
- ASSERT_TRUE(android::base::ReadFully(tmp_output_file.fd, &file_contents[0], file_contents.size()));
- ASSERT_EQ(file_contents, buffer);
-
- for (int i = 0; i < 90072; ++i) {
- const uint8_t* line = &file_contents[0] + (3 * i);
- ASSERT_EQ('a', line[0]);
- ASSERT_EQ('b', line[1]);
- ASSERT_EQ('\n', line[2]);
- }
-}
-
-TEST(ziparchive, TrailerAfterEOCD) {
- TemporaryFile tmp_file;
- ASSERT_NE(-1, tmp_file.fd);
-
- // Create a file with 8 bytes of random garbage.
- static const uint8_t trailer[] = {'A', 'n', 'd', 'r', 'o', 'i', 'd', 'z'};
- ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, kEmptyEntriesZip, sizeof(kEmptyEntriesZip)));
- ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, trailer, sizeof(trailer)));
-
- ZipArchiveHandle handle;
- ASSERT_GT(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle, false));
-}
-
-TEST(ziparchive, ExtractToFile) {
- TemporaryFile tmp_file;
- ASSERT_NE(-1, tmp_file.fd);
- const uint8_t data[8] = {'1', '2', '3', '4', '5', '6', '7', '8'};
- const size_t data_size = sizeof(data);
-
- ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, data, data_size));
-
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
-
- ZipEntry entry;
- ASSERT_EQ(0, FindEntry(handle, "a.txt", &entry));
- ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_file.fd));
-
- // Assert that the first 8 bytes of the file haven't been clobbered.
- uint8_t read_buffer[data_size];
- ASSERT_EQ(0, lseek(tmp_file.fd, 0, SEEK_SET));
- ASSERT_TRUE(android::base::ReadFully(tmp_file.fd, read_buffer, data_size));
- ASSERT_EQ(0, memcmp(read_buffer, data, data_size));
-
- // Assert that the remainder of the file contains the incompressed data.
- std::vector<uint8_t> uncompressed_data(entry.uncompressed_length);
- ASSERT_TRUE(
- android::base::ReadFully(tmp_file.fd, uncompressed_data.data(), entry.uncompressed_length));
- ASSERT_EQ(0, memcmp(&uncompressed_data[0], kATxtContents.data(), kATxtContents.size()));
-
- // Assert that the total length of the file is sane
- ASSERT_EQ(static_cast<ssize_t>(data_size + kATxtContents.size()),
- lseek(tmp_file.fd, 0, SEEK_END));
-}
-
-#if !defined(_WIN32)
-TEST(ziparchive, OpenFromMemory) {
- const std::string zip_path = test_data_dir + "/dummy-update.zip";
- android::base::unique_fd fd(open(zip_path.c_str(), O_RDONLY | O_BINARY));
- ASSERT_NE(-1, fd);
- struct stat sb;
- ASSERT_EQ(0, fstat(fd, &sb));
-
- // Memory map the file first and open the archive from the memory region.
- auto file_map{
- android::base::MappedFile::FromFd(fd, 0, static_cast<size_t>(sb.st_size), PROT_READ)};
- ZipArchiveHandle handle;
- ASSERT_EQ(0,
- OpenArchiveFromMemory(file_map->data(), file_map->size(), zip_path.c_str(), &handle));
-
- // Assert one entry can be found and extracted correctly.
- ZipEntry binary_entry;
- ASSERT_EQ(0, FindEntry(handle, "META-INF/com/google/android/update-binary", &binary_entry));
- TemporaryFile tmp_binary;
- ASSERT_NE(-1, tmp_binary.fd);
- ASSERT_EQ(0, ExtractEntryToFile(handle, &binary_entry, tmp_binary.fd));
-}
-#endif
-
-static void ZipArchiveStreamTest(ZipArchiveHandle& handle, const std::string& entry_name, bool raw,
- bool verified, ZipEntry* entry, std::vector<uint8_t>* read_data) {
- ASSERT_EQ(0, FindEntry(handle, entry_name, entry));
- std::unique_ptr<ZipArchiveStreamEntry> stream;
- if (raw) {
- stream.reset(ZipArchiveStreamEntry::CreateRaw(handle, *entry));
- if (entry->method == kCompressStored) {
- read_data->resize(entry->uncompressed_length);
- } else {
- read_data->resize(entry->compressed_length);
- }
- } else {
- stream.reset(ZipArchiveStreamEntry::Create(handle, *entry));
- read_data->resize(entry->uncompressed_length);
- }
- uint8_t* read_data_ptr = read_data->data();
- ASSERT_TRUE(stream.get() != nullptr);
- const std::vector<uint8_t>* data;
- uint64_t total_size = 0;
- while ((data = stream->Read()) != nullptr) {
- total_size += data->size();
- memcpy(read_data_ptr, data->data(), data->size());
- read_data_ptr += data->size();
- }
- ASSERT_EQ(verified, stream->Verify());
- ASSERT_EQ(total_size, read_data->size());
-}
-
-static void ZipArchiveStreamTestUsingContents(const std::string& zip_file,
- const std::string& entry_name,
- const std::vector<uint8_t>& contents, bool raw) {
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveWrapper(zip_file, &handle));
-
- ZipEntry entry;
- std::vector<uint8_t> read_data;
- ZipArchiveStreamTest(handle, entry_name, raw, true, &entry, &read_data);
-
- ASSERT_EQ(contents.size(), read_data.size());
- ASSERT_TRUE(memcmp(read_data.data(), contents.data(), read_data.size()) == 0);
-
- CloseArchive(handle);
-}
-
-static void ZipArchiveStreamTestUsingMemory(const std::string& zip_file,
- const std::string& entry_name) {
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveWrapper(zip_file, &handle));
-
- ZipEntry entry;
- std::vector<uint8_t> read_data;
- ZipArchiveStreamTest(handle, entry_name, false, true, &entry, &read_data);
-
- std::vector<uint8_t> cmp_data(entry.uncompressed_length);
- ASSERT_EQ(entry.uncompressed_length, read_data.size());
- ASSERT_EQ(
- 0, ExtractToMemory(handle, &entry, cmp_data.data(), static_cast<uint32_t>(cmp_data.size())));
- ASSERT_TRUE(memcmp(read_data.data(), cmp_data.data(), read_data.size()) == 0);
-
- CloseArchive(handle);
-}
-
-TEST(ziparchive, StreamCompressed) {
- ZipArchiveStreamTestUsingContents(kValidZip, "a.txt", kATxtContents, false);
-}
-
-TEST(ziparchive, StreamUncompressed) {
- ZipArchiveStreamTestUsingContents(kValidZip, "b.txt", kBTxtContents, false);
-}
-
-TEST(ziparchive, StreamRawCompressed) {
- ZipArchiveStreamTestUsingContents(kValidZip, "a.txt", kATxtContentsCompressed, true);
-}
-
-TEST(ziparchive, StreamRawUncompressed) {
- ZipArchiveStreamTestUsingContents(kValidZip, "b.txt", kBTxtContents, true);
-}
-
-TEST(ziparchive, StreamLargeCompressed) {
- ZipArchiveStreamTestUsingMemory(kLargeZip, "compress.txt");
-}
-
-TEST(ziparchive, StreamLargeUncompressed) {
- ZipArchiveStreamTestUsingMemory(kLargeZip, "uncompress.txt");
-}
-
-TEST(ziparchive, StreamCompressedBadCrc) {
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveWrapper(kBadCrcZip, &handle));
-
- ZipEntry entry;
- std::vector<uint8_t> read_data;
- ZipArchiveStreamTest(handle, "a.txt", false, false, &entry, &read_data);
-
- CloseArchive(handle);
-}
-
-TEST(ziparchive, StreamUncompressedBadCrc) {
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveWrapper(kBadCrcZip, &handle));
-
- ZipEntry entry;
- std::vector<uint8_t> read_data;
- ZipArchiveStreamTest(handle, "b.txt", false, false, &entry, &read_data);
-
- CloseArchive(handle);
-}
-
-// Generated using the following Java program:
-// public static void main(String[] foo) throws Exception {
-// FileOutputStream fos = new
-// FileOutputStream("/tmp/data_descriptor.zip");
-// ZipOutputStream zos = new ZipOutputStream(fos);
-// ZipEntry ze = new ZipEntry("name");
-// ze.setMethod(ZipEntry.DEFLATED);
-// zos.putNextEntry(ze);
-// zos.write("abdcdefghijk".getBytes());
-// zos.closeEntry();
-// zos.close();
-// }
-//
-// cat /tmp/data_descriptor.zip | xxd -i
-//
-static const std::vector<uint8_t> kDataDescriptorZipFile{
- 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x08, 0x08, 0x00, 0x30, 0x59, 0xce, 0x4a, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6e, 0x61,
- 0x6d, 0x65, 0x4b, 0x4c, 0x4a, 0x49, 0x4e, 0x49, 0x4d, 0x4b, 0xcf, 0xc8, 0xcc, 0xca, 0x06, 0x00,
- //[sig---------------], [crc32---------------], [csize---------------], [size----------------]
- 0x50, 0x4b, 0x07, 0x08, 0x3d, 0x4e, 0x0e, 0xf9, 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
- 0x50, 0x4b, 0x01, 0x02, 0x14, 0x00, 0x14, 0x00, 0x08, 0x08, 0x08, 0x00, 0x30, 0x59, 0xce, 0x4a,
- 0x3d, 0x4e, 0x0e, 0xf9, 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x61,
- 0x6d, 0x65, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x32, 0x00,
- 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00};
-
-// The offsets of the data descriptor in this file, so we can mess with
-// them later in the test.
-static constexpr uint32_t kDataDescriptorOffset = 48;
-static constexpr uint32_t kCSizeOffset = kDataDescriptorOffset + 8;
-static constexpr uint32_t kSizeOffset = kCSizeOffset + 4;
-
-static void ExtractEntryToMemory(const std::vector<uint8_t>& zip_data,
- std::vector<uint8_t>* entry_out, int32_t* error_code_out) {
- TemporaryFile tmp_file;
- ASSERT_NE(-1, tmp_file.fd);
- ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, &zip_data[0], zip_data.size()));
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "ExtractEntryToMemory", &handle, false));
-
- // This function expects a variant of kDataDescriptorZipFile, for look for
- // an entry whose name is "name" and whose size is 12 (contents =
- // "abdcdefghijk").
- ZipEntry entry;
- ASSERT_EQ(0, FindEntry(handle, "name", &entry));
- ASSERT_EQ(static_cast<uint32_t>(12), entry.uncompressed_length);
-
- entry_out->resize(12);
- (*error_code_out) = ExtractToMemory(handle, &entry, &((*entry_out)[0]), 12);
-
- CloseArchive(handle);
-}
-
-TEST(ziparchive, ValidDataDescriptors) {
- std::vector<uint8_t> entry;
- int32_t error_code = 0;
- ExtractEntryToMemory(kDataDescriptorZipFile, &entry, &error_code);
-
- ASSERT_EQ(0, error_code);
- ASSERT_EQ(12u, entry.size());
- ASSERT_EQ('a', entry[0]);
- ASSERT_EQ('k', entry[11]);
-}
-
-TEST(ziparchive, InvalidDataDescriptors_csize) {
- std::vector<uint8_t> invalid_csize = kDataDescriptorZipFile;
- invalid_csize[kCSizeOffset] = 0xfe;
-
- std::vector<uint8_t> entry;
- int32_t error_code = 0;
- ExtractEntryToMemory(invalid_csize, &entry, &error_code);
-
- ASSERT_EQ(kInconsistentInformation, error_code);
-}
-
-TEST(ziparchive, InvalidDataDescriptors_size) {
- std::vector<uint8_t> invalid_size = kDataDescriptorZipFile;
- invalid_size[kSizeOffset] = 0xfe;
-
- std::vector<uint8_t> entry;
- int32_t error_code = 0;
- ExtractEntryToMemory(invalid_size, &entry, &error_code);
-
- ASSERT_EQ(kInconsistentInformation, error_code);
-}
-
-TEST(ziparchive, ErrorCodeString) {
- ASSERT_STREQ("Success", ErrorCodeString(0));
-
- // Out of bounds.
- ASSERT_STREQ("Unknown return code", ErrorCodeString(1));
- ASSERT_STRNE("Unknown return code", ErrorCodeString(kLastErrorCode));
- ASSERT_STREQ("Unknown return code", ErrorCodeString(kLastErrorCode - 1));
-
- ASSERT_STREQ("I/O error", ErrorCodeString(kIoError));
-}
-
-// A zip file whose local file header at offset zero is corrupted.
-//
-// ---------------
-// cat foo > a.txt
-// zip a.zip a.txt
-// cat a.zip | xxd -i
-//
-// Manual changes :
-// [2] = 0xff // Corrupt the LFH signature of entry 0.
-// [3] = 0xff // Corrupt the LFH signature of entry 0.
-static const std::vector<uint8_t> kZipFileWithBrokenLfhSignature{
- //[lfh-sig-----------], [lfh contents---------------------------------
- 0x50, 0x4b, 0xff, 0xff, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x80,
- //--------------------------------------------------------------------
- 0x09, 0x4b, 0xa8, 0x65, 0x32, 0x7e, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00,
- //-------------------------------] [file-name-----------------], [---
- 0x00, 0x00, 0x05, 0x00, 0x1c, 0x00, 0x61, 0x2e, 0x74, 0x78, 0x74, 0x55,
- // entry-contents------------------------------------------------------
- 0x54, 0x09, 0x00, 0x03, 0x51, 0x24, 0x8b, 0x59, 0x51, 0x24, 0x8b, 0x59,
- //--------------------------------------------------------------------
- 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0x89, 0x42, 0x00, 0x00, 0x04, 0x88,
- //-------------------------------------], [cd-record-sig-------], [---
- 0x13, 0x00, 0x00, 0x66, 0x6f, 0x6f, 0x0a, 0x50, 0x4b, 0x01, 0x02, 0x1e,
- // cd-record-----------------------------------------------------------
- 0x03, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x80, 0x09, 0x4b, 0xa8,
- //--------------------------------------------------------------------
- 0x65, 0x32, 0x7e, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05,
- //--------------------------------------------------------------------
- 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xa0,
- //-] [lfh-file-header-off-], [file-name-----------------], [extra----
- 0x81, 0x00, 0x00, 0x00, 0x00, 0x61, 0x2e, 0x74, 0x78, 0x74, 0x55, 0x54,
- //--------------------------------------------------------------------
- 0x05, 0x00, 0x03, 0x51, 0x24, 0x8b, 0x59, 0x75, 0x78, 0x0b, 0x00, 0x01,
- //-------------------------------------------------------], [eocd-sig-
- 0x04, 0x89, 0x42, 0x00, 0x00, 0x04, 0x88, 0x13, 0x00, 0x00, 0x50, 0x4b,
- //-------], [---------------------------------------------------------
- 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x4b, 0x00,
- //-------------------------------------------]
- 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00};
-
-TEST(ziparchive, BrokenLfhSignature) {
- TemporaryFile tmp_file;
- ASSERT_NE(-1, tmp_file.fd);
- ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, &kZipFileWithBrokenLfhSignature[0],
- kZipFileWithBrokenLfhSignature.size()));
- ZipArchiveHandle handle;
- ASSERT_EQ(kInvalidFile, OpenArchiveFd(tmp_file.fd, "LeadingNonZipBytes", &handle, false));
-}
-
-class VectorReader : public zip_archive::Reader {
- public:
- VectorReader(const std::vector<uint8_t>& input) : Reader(), input_(input) {}
-
- bool ReadAtOffset(uint8_t* buf, size_t len, uint32_t offset) const {
- if ((offset + len) < input_.size()) {
- return false;
- }
-
- memcpy(buf, &input_[offset], len);
- return true;
- }
-
- private:
- const std::vector<uint8_t>& input_;
-};
-
-class VectorWriter : public zip_archive::Writer {
- public:
- VectorWriter() : Writer() {}
-
- bool Append(uint8_t* buf, size_t size) {
- output_.insert(output_.end(), buf, buf + size);
- return true;
- }
-
- std::vector<uint8_t>& GetOutput() { return output_; }
-
- private:
- std::vector<uint8_t> output_;
-};
-
-class BadReader : public zip_archive::Reader {
- public:
- BadReader() : Reader() {}
-
- bool ReadAtOffset(uint8_t*, size_t, uint32_t) const { return false; }
-};
-
-class BadWriter : public zip_archive::Writer {
- public:
- BadWriter() : Writer() {}
-
- bool Append(uint8_t*, size_t) { return false; }
-};
-
-TEST(ziparchive, Inflate) {
- const uint32_t compressed_length = static_cast<uint32_t>(kATxtContentsCompressed.size());
- const uint32_t uncompressed_length = static_cast<uint32_t>(kATxtContents.size());
-
- const VectorReader reader(kATxtContentsCompressed);
- {
- VectorWriter writer;
- uint64_t crc_out = 0;
-
- int32_t ret =
- zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, &crc_out);
- ASSERT_EQ(0, ret);
- ASSERT_EQ(kATxtContents, writer.GetOutput());
- ASSERT_EQ(0x950821C5u, crc_out);
- }
-
- {
- VectorWriter writer;
- int32_t ret =
- zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr);
- ASSERT_EQ(0, ret);
- ASSERT_EQ(kATxtContents, writer.GetOutput());
- }
-
- {
- BadWriter writer;
- int32_t ret =
- zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr);
- ASSERT_EQ(kIoError, ret);
- }
-
- {
- BadReader reader;
- VectorWriter writer;
- int32_t ret =
- zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr);
- ASSERT_EQ(kIoError, ret);
- ASSERT_EQ(0u, writer.GetOutput().size());
- }
-}
diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc
deleted file mode 100644
index 67279a6..0000000
--- a/libziparchive/zip_writer.cc
+++ /dev/null
@@ -1,582 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ziparchive/zip_writer.h"
-
-#include <sys/param.h>
-#include <sys/stat.h>
-#include <zlib.h>
-#include <cstdio>
-#define DEF_MEM_LEVEL 8 // normally in zutil.h?
-
-#include <memory>
-#include <vector>
-
-#include "android-base/logging.h"
-
-#include "entry_name_utils-inl.h"
-#include "zip_archive_common.h"
-
-#undef powerof2
-#define powerof2(x) \
- ({ \
- __typeof__(x) _x = (x); \
- __typeof__(x) _x2; \
- __builtin_add_overflow(_x, -1, &_x2) ? 1 : ((_x2 & _x) == 0); \
- })
-
-/* Zip compression methods we support */
-enum {
- kCompressStored = 0, // no compression
- kCompressDeflated = 8, // standard deflate
-};
-
-// Size of the output buffer used for compression.
-static const size_t kBufSize = 32768u;
-
-// No error, operation completed successfully.
-static const int32_t kNoError = 0;
-
-// The ZipWriter is in a bad state.
-static const int32_t kInvalidState = -1;
-
-// There was an IO error while writing to disk.
-static const int32_t kIoError = -2;
-
-// The zip entry name was invalid.
-static const int32_t kInvalidEntryName = -3;
-
-// An error occurred in zlib.
-static const int32_t kZlibError = -4;
-
-// The start aligned function was called with the aligned flag.
-static const int32_t kInvalidAlign32Flag = -5;
-
-// The alignment parameter is not a power of 2.
-static const int32_t kInvalidAlignment = -6;
-
-static const char* sErrorCodes[] = {
- "Invalid state", "IO error", "Invalid entry name", "Zlib error",
-};
-
-const char* ZipWriter::ErrorCodeString(int32_t error_code) {
- if (error_code < 0 && (-error_code) < static_cast<int32_t>(arraysize(sErrorCodes))) {
- return sErrorCodes[-error_code];
- }
- return nullptr;
-}
-
-static void DeleteZStream(z_stream* stream) {
- deflateEnd(stream);
- delete stream;
-}
-
-ZipWriter::ZipWriter(FILE* f)
- : file_(f),
- seekable_(false),
- current_offset_(0),
- state_(State::kWritingZip),
- z_stream_(nullptr, DeleteZStream),
- buffer_(kBufSize) {
- // Check if the file is seekable (regular file). If fstat fails, that's fine, subsequent calls
- // will fail as well.
- struct stat file_stats;
- if (fstat(fileno(f), &file_stats) == 0) {
- seekable_ = S_ISREG(file_stats.st_mode);
- }
-}
-
-ZipWriter::ZipWriter(ZipWriter&& writer) noexcept
- : file_(writer.file_),
- seekable_(writer.seekable_),
- current_offset_(writer.current_offset_),
- state_(writer.state_),
- files_(std::move(writer.files_)),
- z_stream_(std::move(writer.z_stream_)),
- buffer_(std::move(writer.buffer_)) {
- writer.file_ = nullptr;
- writer.state_ = State::kError;
-}
-
-ZipWriter& ZipWriter::operator=(ZipWriter&& writer) noexcept {
- file_ = writer.file_;
- seekable_ = writer.seekable_;
- current_offset_ = writer.current_offset_;
- state_ = writer.state_;
- files_ = std::move(writer.files_);
- z_stream_ = std::move(writer.z_stream_);
- buffer_ = std::move(writer.buffer_);
- writer.file_ = nullptr;
- writer.state_ = State::kError;
- return *this;
-}
-
-int32_t ZipWriter::HandleError(int32_t error_code) {
- state_ = State::kError;
- z_stream_.reset();
- return error_code;
-}
-
-int32_t ZipWriter::StartEntry(std::string_view path, size_t flags) {
- uint32_t alignment = 0;
- if (flags & kAlign32) {
- flags &= ~kAlign32;
- alignment = 4;
- }
- return StartAlignedEntryWithTime(path, flags, time_t(), alignment);
-}
-
-int32_t ZipWriter::StartAlignedEntry(std::string_view path, size_t flags, uint32_t alignment) {
- return StartAlignedEntryWithTime(path, flags, time_t(), alignment);
-}
-
-int32_t ZipWriter::StartEntryWithTime(std::string_view path, size_t flags, time_t time) {
- uint32_t alignment = 0;
- if (flags & kAlign32) {
- flags &= ~kAlign32;
- alignment = 4;
- }
- return StartAlignedEntryWithTime(path, flags, time, alignment);
-}
-
-static void ExtractTimeAndDate(time_t when, uint16_t* out_time, uint16_t* out_date) {
- /* round up to an even number of seconds */
- when = static_cast<time_t>((static_cast<unsigned long>(when) + 1) & (~1));
-
- struct tm* ptm;
-#if !defined(_WIN32)
- struct tm tm_result;
- ptm = localtime_r(&when, &tm_result);
-#else
- ptm = localtime(&when);
-#endif
-
- int year = ptm->tm_year;
- if (year < 80) {
- year = 80;
- }
-
- *out_date = static_cast<uint16_t>((year - 80) << 9 | (ptm->tm_mon + 1) << 5 | ptm->tm_mday);
- *out_time = static_cast<uint16_t>(ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1);
-}
-
-static void CopyFromFileEntry(const ZipWriter::FileEntry& src, bool use_data_descriptor,
- LocalFileHeader* dst) {
- dst->lfh_signature = LocalFileHeader::kSignature;
- if (use_data_descriptor) {
- // Set this flag to denote that a DataDescriptor struct will appear after the data,
- // containing the crc and size fields.
- dst->gpb_flags |= kGPBDDFlagMask;
-
- // The size and crc fields must be 0.
- dst->compressed_size = 0u;
- dst->uncompressed_size = 0u;
- dst->crc32 = 0u;
- } else {
- dst->compressed_size = src.compressed_size;
- dst->uncompressed_size = src.uncompressed_size;
- dst->crc32 = src.crc32;
- }
- dst->compression_method = src.compression_method;
- dst->last_mod_time = src.last_mod_time;
- dst->last_mod_date = src.last_mod_date;
- DCHECK_LE(src.path.size(), std::numeric_limits<uint16_t>::max());
- dst->file_name_length = static_cast<uint16_t>(src.path.size());
- dst->extra_field_length = src.padding_length;
-}
-
-int32_t ZipWriter::StartAlignedEntryWithTime(std::string_view path, size_t flags, time_t time,
- uint32_t alignment) {
- if (state_ != State::kWritingZip) {
- return kInvalidState;
- }
-
- // Can only have 16535 entries because of zip records.
- if (files_.size() == std::numeric_limits<uint16_t>::max()) {
- return HandleError(kIoError);
- }
-
- if (flags & kAlign32) {
- return kInvalidAlign32Flag;
- }
-
- if (powerof2(alignment) == 0) {
- return kInvalidAlignment;
- }
- if (alignment > std::numeric_limits<uint16_t>::max()) {
- return kInvalidAlignment;
- }
-
- FileEntry file_entry = {};
- file_entry.local_file_header_offset = current_offset_;
- file_entry.path = path;
- // No support for larger than 4GB files.
- if (file_entry.local_file_header_offset > std::numeric_limits<uint32_t>::max()) {
- return HandleError(kIoError);
- }
-
- if (!IsValidEntryName(reinterpret_cast<const uint8_t*>(file_entry.path.data()),
- file_entry.path.size())) {
- return kInvalidEntryName;
- }
-
- if (flags & ZipWriter::kCompress) {
- file_entry.compression_method = kCompressDeflated;
-
- int32_t result = PrepareDeflate();
- if (result != kNoError) {
- return result;
- }
- } else {
- file_entry.compression_method = kCompressStored;
- }
-
- ExtractTimeAndDate(time, &file_entry.last_mod_time, &file_entry.last_mod_date);
-
- off_t offset = current_offset_ + sizeof(LocalFileHeader) + file_entry.path.size();
- // prepare a pre-zeroed memory page in case when we need to pad some aligned data.
- static constexpr auto kPageSize = 4096;
- static constexpr char kSmallZeroPadding[kPageSize] = {};
- // use this buffer if our preallocated one is too small
- std::vector<char> zero_padding_big;
- const char* zero_padding = nullptr;
-
- if (alignment != 0 && (offset & (alignment - 1))) {
- // Pad the extra field so the data will be aligned.
- uint16_t padding = static_cast<uint16_t>(alignment - (offset % alignment));
- file_entry.padding_length = padding;
- offset += padding;
- if (padding <= std::size(kSmallZeroPadding)) {
- zero_padding = kSmallZeroPadding;
- } else {
- zero_padding_big.resize(padding, 0);
- zero_padding = zero_padding_big.data();
- }
- }
-
- LocalFileHeader header = {};
- // Always start expecting a data descriptor. When the data has finished being written,
- // if it is possible to seek back, the GPB flag will reset and the sizes written.
- CopyFromFileEntry(file_entry, true /*use_data_descriptor*/, &header);
-
- if (fwrite(&header, sizeof(header), 1, file_) != 1) {
- return HandleError(kIoError);
- }
-
- if (fwrite(path.data(), 1, path.size(), file_) != path.size()) {
- return HandleError(kIoError);
- }
-
- if (file_entry.padding_length != 0 && fwrite(zero_padding, 1, file_entry.padding_length,
- file_) != file_entry.padding_length) {
- return HandleError(kIoError);
- }
-
- current_file_entry_ = std::move(file_entry);
- current_offset_ = offset;
- state_ = State::kWritingEntry;
- return kNoError;
-}
-
-int32_t ZipWriter::DiscardLastEntry() {
- if (state_ != State::kWritingZip || files_.empty()) {
- return kInvalidState;
- }
-
- FileEntry& last_entry = files_.back();
- current_offset_ = last_entry.local_file_header_offset;
- if (fseeko(file_, current_offset_, SEEK_SET) != 0) {
- return HandleError(kIoError);
- }
- files_.pop_back();
- return kNoError;
-}
-
-int32_t ZipWriter::GetLastEntry(FileEntry* out_entry) {
- CHECK(out_entry != nullptr);
-
- if (files_.empty()) {
- return kInvalidState;
- }
- *out_entry = files_.back();
- return kNoError;
-}
-
-int32_t ZipWriter::PrepareDeflate() {
- CHECK(state_ == State::kWritingZip);
-
- // Initialize the z_stream for compression.
- z_stream_ = std::unique_ptr<z_stream, void (*)(z_stream*)>(new z_stream(), DeleteZStream);
-
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wold-style-cast"
- int zerr = deflateInit2(z_stream_.get(), Z_BEST_COMPRESSION, Z_DEFLATED, -MAX_WBITS,
- DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
-#pragma GCC diagnostic pop
-
- if (zerr != Z_OK) {
- if (zerr == Z_VERSION_ERROR) {
- LOG(ERROR) << "Installed zlib is not compatible with linked version (" << ZLIB_VERSION << ")";
- return HandleError(kZlibError);
- } else {
- LOG(ERROR) << "deflateInit2 failed (zerr=" << zerr << ")";
- return HandleError(kZlibError);
- }
- }
-
- z_stream_->next_out = buffer_.data();
- DCHECK_EQ(buffer_.size(), kBufSize);
- z_stream_->avail_out = static_cast<uint32_t>(buffer_.size());
- return kNoError;
-}
-
-int32_t ZipWriter::WriteBytes(const void* data, size_t len) {
- if (state_ != State::kWritingEntry) {
- return HandleError(kInvalidState);
- }
- // Need to be able to mark down data correctly.
- if (len + static_cast<uint64_t>(current_file_entry_.uncompressed_size) >
- std::numeric_limits<uint32_t>::max()) {
- return HandleError(kIoError);
- }
- uint32_t len32 = static_cast<uint32_t>(len);
-
- int32_t result = kNoError;
- if (current_file_entry_.compression_method & kCompressDeflated) {
- result = CompressBytes(¤t_file_entry_, data, len32);
- } else {
- result = StoreBytes(¤t_file_entry_, data, len32);
- }
-
- if (result != kNoError) {
- return result;
- }
-
- current_file_entry_.crc32 = static_cast<uint32_t>(
- crc32(current_file_entry_.crc32, reinterpret_cast<const Bytef*>(data), len32));
- current_file_entry_.uncompressed_size += len32;
- return kNoError;
-}
-
-int32_t ZipWriter::StoreBytes(FileEntry* file, const void* data, uint32_t len) {
- CHECK(state_ == State::kWritingEntry);
-
- if (fwrite(data, 1, len, file_) != len) {
- return HandleError(kIoError);
- }
- file->compressed_size += len;
- current_offset_ += len;
- return kNoError;
-}
-
-int32_t ZipWriter::CompressBytes(FileEntry* file, const void* data, uint32_t len) {
- CHECK(state_ == State::kWritingEntry);
- CHECK(z_stream_);
- CHECK(z_stream_->next_out != nullptr);
- CHECK(z_stream_->avail_out != 0);
-
- // Prepare the input.
- z_stream_->next_in = reinterpret_cast<const uint8_t*>(data);
- z_stream_->avail_in = len;
-
- while (z_stream_->avail_in > 0) {
- // We have more data to compress.
- int zerr = deflate(z_stream_.get(), Z_NO_FLUSH);
- if (zerr != Z_OK) {
- return HandleError(kZlibError);
- }
-
- if (z_stream_->avail_out == 0) {
- // The output is full, let's write it to disk.
- size_t write_bytes = z_stream_->next_out - buffer_.data();
- if (fwrite(buffer_.data(), 1, write_bytes, file_) != write_bytes) {
- return HandleError(kIoError);
- }
- file->compressed_size += write_bytes;
- current_offset_ += write_bytes;
-
- // Reset the output buffer for the next input.
- z_stream_->next_out = buffer_.data();
- DCHECK_EQ(buffer_.size(), kBufSize);
- z_stream_->avail_out = static_cast<uint32_t>(buffer_.size());
- }
- }
- return kNoError;
-}
-
-int32_t ZipWriter::FlushCompressedBytes(FileEntry* file) {
- CHECK(state_ == State::kWritingEntry);
- CHECK(z_stream_);
- CHECK(z_stream_->next_out != nullptr);
- CHECK(z_stream_->avail_out != 0);
-
- // Keep deflating while there isn't enough space in the buffer to
- // to complete the compress.
- int zerr;
- while ((zerr = deflate(z_stream_.get(), Z_FINISH)) == Z_OK) {
- CHECK(z_stream_->avail_out == 0);
- size_t write_bytes = z_stream_->next_out - buffer_.data();
- if (fwrite(buffer_.data(), 1, write_bytes, file_) != write_bytes) {
- return HandleError(kIoError);
- }
- file->compressed_size += write_bytes;
- current_offset_ += write_bytes;
-
- z_stream_->next_out = buffer_.data();
- DCHECK_EQ(buffer_.size(), kBufSize);
- z_stream_->avail_out = static_cast<uint32_t>(buffer_.size());
- }
- if (zerr != Z_STREAM_END) {
- return HandleError(kZlibError);
- }
-
- size_t write_bytes = z_stream_->next_out - buffer_.data();
- if (write_bytes != 0) {
- if (fwrite(buffer_.data(), 1, write_bytes, file_) != write_bytes) {
- return HandleError(kIoError);
- }
- file->compressed_size += write_bytes;
- current_offset_ += write_bytes;
- }
- z_stream_.reset();
- return kNoError;
-}
-
-bool ZipWriter::ShouldUseDataDescriptor() const {
- // Only use a trailing "data descriptor" if the output isn't seekable.
- return !seekable_;
-}
-
-int32_t ZipWriter::FinishEntry() {
- if (state_ != State::kWritingEntry) {
- return kInvalidState;
- }
-
- if (current_file_entry_.compression_method & kCompressDeflated) {
- int32_t result = FlushCompressedBytes(¤t_file_entry_);
- if (result != kNoError) {
- return result;
- }
- }
-
- if (ShouldUseDataDescriptor()) {
- // Some versions of ZIP don't allow STORED data to have a trailing DataDescriptor.
- // If this file is not seekable, or if the data is compressed, write a DataDescriptor.
- const uint32_t sig = DataDescriptor::kOptSignature;
- if (fwrite(&sig, sizeof(sig), 1, file_) != 1) {
- return HandleError(kIoError);
- }
-
- DataDescriptor dd = {};
- dd.crc32 = current_file_entry_.crc32;
- dd.compressed_size = current_file_entry_.compressed_size;
- dd.uncompressed_size = current_file_entry_.uncompressed_size;
- if (fwrite(&dd, sizeof(dd), 1, file_) != 1) {
- return HandleError(kIoError);
- }
- current_offset_ += sizeof(DataDescriptor::kOptSignature) + sizeof(dd);
- } else {
- // Seek back to the header and rewrite to include the size.
- if (fseeko(file_, current_file_entry_.local_file_header_offset, SEEK_SET) != 0) {
- return HandleError(kIoError);
- }
-
- LocalFileHeader header = {};
- CopyFromFileEntry(current_file_entry_, false /*use_data_descriptor*/, &header);
-
- if (fwrite(&header, sizeof(header), 1, file_) != 1) {
- return HandleError(kIoError);
- }
-
- if (fseeko(file_, current_offset_, SEEK_SET) != 0) {
- return HandleError(kIoError);
- }
- }
-
- files_.emplace_back(std::move(current_file_entry_));
- state_ = State::kWritingZip;
- return kNoError;
-}
-
-int32_t ZipWriter::Finish() {
- if (state_ != State::kWritingZip) {
- return kInvalidState;
- }
-
- off_t startOfCdr = current_offset_;
- for (FileEntry& file : files_) {
- CentralDirectoryRecord cdr = {};
- cdr.record_signature = CentralDirectoryRecord::kSignature;
- if (ShouldUseDataDescriptor()) {
- cdr.gpb_flags |= kGPBDDFlagMask;
- }
- cdr.compression_method = file.compression_method;
- cdr.last_mod_time = file.last_mod_time;
- cdr.last_mod_date = file.last_mod_date;
- cdr.crc32 = file.crc32;
- cdr.compressed_size = file.compressed_size;
- cdr.uncompressed_size = file.uncompressed_size;
- // Checked in IsValidEntryName.
- DCHECK_LE(file.path.size(), std::numeric_limits<uint16_t>::max());
- cdr.file_name_length = static_cast<uint16_t>(file.path.size());
- // Checked in StartAlignedEntryWithTime.
- DCHECK_LE(file.local_file_header_offset, std::numeric_limits<uint32_t>::max());
- cdr.local_file_header_offset = static_cast<uint32_t>(file.local_file_header_offset);
- if (fwrite(&cdr, sizeof(cdr), 1, file_) != 1) {
- return HandleError(kIoError);
- }
-
- if (fwrite(file.path.data(), 1, file.path.size(), file_) != file.path.size()) {
- return HandleError(kIoError);
- }
-
- current_offset_ += sizeof(cdr) + file.path.size();
- }
-
- EocdRecord er = {};
- er.eocd_signature = EocdRecord::kSignature;
- er.disk_num = 0;
- er.cd_start_disk = 0;
- // Checked when adding entries.
- DCHECK_LE(files_.size(), std::numeric_limits<uint16_t>::max());
- er.num_records_on_disk = static_cast<uint16_t>(files_.size());
- er.num_records = static_cast<uint16_t>(files_.size());
- if (current_offset_ > std::numeric_limits<uint32_t>::max()) {
- return HandleError(kIoError);
- }
- er.cd_size = static_cast<uint32_t>(current_offset_ - startOfCdr);
- er.cd_start_offset = static_cast<uint32_t>(startOfCdr);
-
- if (fwrite(&er, sizeof(er), 1, file_) != 1) {
- return HandleError(kIoError);
- }
-
- current_offset_ += sizeof(er);
-
- // Since we can BackUp() and potentially finish writing at an offset less than one we had
- // already written at, we must truncate the file.
-
- if (ftruncate(fileno(file_), current_offset_) != 0) {
- return HandleError(kIoError);
- }
-
- if (fflush(file_) != 0) {
- return HandleError(kIoError);
- }
-
- state_ = State::kDone;
- return kNoError;
-}
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc
deleted file mode 100644
index d324d4b..0000000
--- a/libziparchive/zip_writer_test.cc
+++ /dev/null
@@ -1,428 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ziparchive/zip_writer.h"
-#include "ziparchive/zip_archive.h"
-
-#include <android-base/test_utils.h>
-#include <gtest/gtest.h>
-#include <time.h>
-#include <memory>
-#include <vector>
-
-static ::testing::AssertionResult AssertFileEntryContentsEq(const std::string& expected,
- ZipArchiveHandle handle,
- ZipEntry* zip_entry);
-
-struct zipwriter : public ::testing::Test {
- TemporaryFile* temp_file_;
- int fd_;
- FILE* file_;
-
- void SetUp() override {
- temp_file_ = new TemporaryFile();
- fd_ = temp_file_->fd;
- file_ = fdopen(fd_, "w");
- ASSERT_NE(file_, nullptr);
- }
-
- void TearDown() override {
- fclose(file_);
- delete temp_file_;
- }
-};
-
-TEST_F(zipwriter, WriteUncompressedZipWithOneFile) {
- ZipWriter writer(file_);
-
- const char* expected = "hello";
-
- ASSERT_EQ(0, writer.StartEntry("file.txt", 0));
- ASSERT_EQ(0, writer.WriteBytes("he", 2));
- ASSERT_EQ(0, writer.WriteBytes("llo", 3));
- ASSERT_EQ(0, writer.FinishEntry());
- ASSERT_EQ(0, writer.Finish());
-
- ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
-
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
-
- ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
- EXPECT_EQ(kCompressStored, data.method);
- EXPECT_EQ(0u, data.has_data_descriptor);
- EXPECT_EQ(strlen(expected), data.compressed_length);
- ASSERT_EQ(strlen(expected), data.uncompressed_length);
- ASSERT_TRUE(AssertFileEntryContentsEq(expected, handle, &data));
-
- CloseArchive(handle);
-}
-
-TEST_F(zipwriter, WriteUncompressedZipWithMultipleFiles) {
- ZipWriter writer(file_);
-
- ASSERT_EQ(0, writer.StartEntry("file.txt", 0));
- ASSERT_EQ(0, writer.WriteBytes("he", 2));
- ASSERT_EQ(0, writer.FinishEntry());
-
- ASSERT_EQ(0, writer.StartEntry("file/file.txt", 0));
- ASSERT_EQ(0, writer.WriteBytes("llo", 3));
- ASSERT_EQ(0, writer.FinishEntry());
-
- ASSERT_EQ(0, writer.StartEntry("file/file2.txt", 0));
- ASSERT_EQ(0, writer.FinishEntry());
-
- ASSERT_EQ(0, writer.Finish());
-
- ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
-
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
-
- ZipEntry data;
-
- ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
- EXPECT_EQ(kCompressStored, data.method);
- EXPECT_EQ(2u, data.compressed_length);
- ASSERT_EQ(2u, data.uncompressed_length);
- ASSERT_TRUE(AssertFileEntryContentsEq("he", handle, &data));
-
- ASSERT_EQ(0, FindEntry(handle, "file/file.txt", &data));
- EXPECT_EQ(kCompressStored, data.method);
- EXPECT_EQ(3u, data.compressed_length);
- ASSERT_EQ(3u, data.uncompressed_length);
- ASSERT_TRUE(AssertFileEntryContentsEq("llo", handle, &data));
-
- ASSERT_EQ(0, FindEntry(handle, "file/file2.txt", &data));
- EXPECT_EQ(kCompressStored, data.method);
- EXPECT_EQ(0u, data.compressed_length);
- EXPECT_EQ(0u, data.uncompressed_length);
-
- CloseArchive(handle);
-}
-
-TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedFlag) {
- ZipWriter writer(file_);
-
- ASSERT_EQ(0, writer.StartEntry("align.txt", ZipWriter::kAlign32));
- ASSERT_EQ(0, writer.WriteBytes("he", 2));
- ASSERT_EQ(0, writer.FinishEntry());
- ASSERT_EQ(0, writer.Finish());
-
- ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
-
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
-
- ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
- EXPECT_EQ(0, data.offset & 0x03);
-
- CloseArchive(handle);
-}
-
-static struct tm MakeTm() {
- struct tm tm;
- memset(&tm, 0, sizeof(struct tm));
- tm.tm_year = 2001 - 1900;
- tm.tm_mon = 1;
- tm.tm_mday = 12;
- tm.tm_hour = 18;
- tm.tm_min = 30;
- tm.tm_sec = 20;
- return tm;
-}
-
-TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedFlagAndTime) {
- ZipWriter writer(file_);
-
- struct tm tm = MakeTm();
- time_t time = mktime(&tm);
- ASSERT_EQ(0, writer.StartEntryWithTime("align.txt", ZipWriter::kAlign32, time));
- ASSERT_EQ(0, writer.WriteBytes("he", 2));
- ASSERT_EQ(0, writer.FinishEntry());
- ASSERT_EQ(0, writer.Finish());
-
- ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
-
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
-
- ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
- EXPECT_EQ(0, data.offset & 0x03);
-
- struct tm mod = data.GetModificationTime();
- EXPECT_EQ(tm.tm_sec, mod.tm_sec);
- EXPECT_EQ(tm.tm_min, mod.tm_min);
- EXPECT_EQ(tm.tm_hour, mod.tm_hour);
- EXPECT_EQ(tm.tm_mday, mod.tm_mday);
- EXPECT_EQ(tm.tm_mon, mod.tm_mon);
- EXPECT_EQ(tm.tm_year, mod.tm_year);
-
- CloseArchive(handle);
-}
-
-TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedValue) {
- ZipWriter writer(file_);
-
- ASSERT_EQ(0, writer.StartAlignedEntry("align.txt", 0, 4096));
- ASSERT_EQ(0, writer.WriteBytes("he", 2));
- ASSERT_EQ(0, writer.FinishEntry());
- ASSERT_EQ(0, writer.Finish());
-
- ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
-
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
-
- ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
- EXPECT_EQ(0, data.offset & 0xfff);
-
- CloseArchive(handle);
-}
-
-TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedValueAndTime) {
- ZipWriter writer(file_);
-
- struct tm tm = MakeTm();
- time_t time = mktime(&tm);
- ASSERT_EQ(0, writer.StartAlignedEntryWithTime("align.txt", 0, time, 4096));
- ASSERT_EQ(0, writer.WriteBytes("he", 2));
- ASSERT_EQ(0, writer.FinishEntry());
- ASSERT_EQ(0, writer.Finish());
-
- ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
-
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
-
- ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
- EXPECT_EQ(0, data.offset & 0xfff);
-
- struct tm mod = data.GetModificationTime();
- EXPECT_EQ(tm.tm_sec, mod.tm_sec);
- EXPECT_EQ(tm.tm_min, mod.tm_min);
- EXPECT_EQ(tm.tm_hour, mod.tm_hour);
- EXPECT_EQ(tm.tm_mday, mod.tm_mday);
- EXPECT_EQ(tm.tm_mon, mod.tm_mon);
- EXPECT_EQ(tm.tm_year, mod.tm_year);
-
- CloseArchive(handle);
-}
-
-TEST_F(zipwriter, WriteCompressedZipWithOneFile) {
- ZipWriter writer(file_);
-
- ASSERT_EQ(0, writer.StartEntry("file.txt", ZipWriter::kCompress));
- ASSERT_EQ(0, writer.WriteBytes("helo", 4));
- ASSERT_EQ(0, writer.FinishEntry());
- ASSERT_EQ(0, writer.Finish());
-
- ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
-
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
-
- ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
- EXPECT_EQ(kCompressDeflated, data.method);
- EXPECT_EQ(0u, data.has_data_descriptor);
- ASSERT_EQ(4u, data.uncompressed_length);
- ASSERT_TRUE(AssertFileEntryContentsEq("helo", handle, &data));
-
- CloseArchive(handle);
-}
-
-TEST_F(zipwriter, WriteCompressedZipFlushFull) {
- // This exact data will cause the Finish() to require multiple calls
- // to deflate() because the ZipWriter buffer isn't big enough to hold
- // the entire compressed data buffer.
- constexpr size_t kBufSize = 10000000;
- std::vector<uint8_t> buffer(kBufSize);
- size_t prev = 1;
- for (size_t i = 0; i < kBufSize; i++) {
- buffer[i] = static_cast<uint8_t>(i + prev);
- prev = i;
- }
-
- ZipWriter writer(file_);
- ASSERT_EQ(0, writer.StartEntry("file.txt", ZipWriter::kCompress));
- ASSERT_EQ(0, writer.WriteBytes(buffer.data(), buffer.size()));
- ASSERT_EQ(0, writer.FinishEntry());
- ASSERT_EQ(0, writer.Finish());
-
- ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
-
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
-
- ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
- EXPECT_EQ(kCompressDeflated, data.method);
- EXPECT_EQ(kBufSize, data.uncompressed_length);
-
- std::vector<uint8_t> decompress(kBufSize);
- memset(decompress.data(), 0, kBufSize);
- ASSERT_EQ(0, ExtractToMemory(handle, &data, decompress.data(),
- static_cast<uint32_t>(decompress.size())));
- EXPECT_EQ(0, memcmp(decompress.data(), buffer.data(), kBufSize))
- << "Input buffer and output buffer are different.";
-
- CloseArchive(handle);
-}
-
-TEST_F(zipwriter, CheckStartEntryErrors) {
- ZipWriter writer(file_);
-
- ASSERT_EQ(-5, writer.StartAlignedEntry("align.txt", ZipWriter::kAlign32, 4096));
- ASSERT_EQ(-6, writer.StartAlignedEntry("align.txt", 0, 3));
-}
-
-TEST_F(zipwriter, BackupRemovesTheLastFile) {
- ZipWriter writer(file_);
-
- const char* kKeepThis = "keep this";
- const char* kDropThis = "drop this";
- const char* kReplaceWithThis = "replace with this";
-
- ZipWriter::FileEntry entry;
- EXPECT_LT(writer.GetLastEntry(&entry), 0);
-
- ASSERT_EQ(0, writer.StartEntry("keep.txt", 0));
- ASSERT_EQ(0, writer.WriteBytes(kKeepThis, strlen(kKeepThis)));
- ASSERT_EQ(0, writer.FinishEntry());
-
- ASSERT_EQ(0, writer.GetLastEntry(&entry));
- EXPECT_EQ("keep.txt", entry.path);
-
- ASSERT_EQ(0, writer.StartEntry("drop.txt", 0));
- ASSERT_EQ(0, writer.WriteBytes(kDropThis, strlen(kDropThis)));
- ASSERT_EQ(0, writer.FinishEntry());
-
- ASSERT_EQ(0, writer.GetLastEntry(&entry));
- EXPECT_EQ("drop.txt", entry.path);
-
- ASSERT_EQ(0, writer.DiscardLastEntry());
-
- ASSERT_EQ(0, writer.GetLastEntry(&entry));
- EXPECT_EQ("keep.txt", entry.path);
-
- ASSERT_EQ(0, writer.StartEntry("replace.txt", 0));
- ASSERT_EQ(0, writer.WriteBytes(kReplaceWithThis, strlen(kReplaceWithThis)));
- ASSERT_EQ(0, writer.FinishEntry());
-
- ASSERT_EQ(0, writer.GetLastEntry(&entry));
- EXPECT_EQ("replace.txt", entry.path);
-
- ASSERT_EQ(0, writer.Finish());
-
- // Verify that "drop.txt" does not exist.
-
- ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
-
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
-
- ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, "keep.txt", &data));
- ASSERT_TRUE(AssertFileEntryContentsEq(kKeepThis, handle, &data));
-
- ASSERT_NE(0, FindEntry(handle, "drop.txt", &data));
-
- ASSERT_EQ(0, FindEntry(handle, "replace.txt", &data));
- ASSERT_TRUE(AssertFileEntryContentsEq(kReplaceWithThis, handle, &data));
-
- CloseArchive(handle);
-}
-
-TEST_F(zipwriter, WriteToUnseekableFile) {
- const char* expected = "hello";
- ZipWriter writer(file_);
- writer.seekable_ = false;
-
- ASSERT_EQ(0, writer.StartEntry("file.txt", 0));
- ASSERT_EQ(0, writer.WriteBytes(expected, strlen(expected)));
- ASSERT_EQ(0, writer.FinishEntry());
- ASSERT_EQ(0, writer.Finish());
- ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
-
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
- ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
- EXPECT_EQ(kCompressStored, data.method);
- EXPECT_EQ(1u, data.has_data_descriptor);
- EXPECT_EQ(strlen(expected), data.compressed_length);
- ASSERT_EQ(strlen(expected), data.uncompressed_length);
- ASSERT_TRUE(AssertFileEntryContentsEq(expected, handle, &data));
- CloseArchive(handle);
-}
-
-TEST_F(zipwriter, TruncateFileAfterBackup) {
- ZipWriter writer(file_);
-
- const char* kSmall = "small";
-
- ASSERT_EQ(0, writer.StartEntry("small.txt", 0));
- ASSERT_EQ(0, writer.WriteBytes(kSmall, strlen(kSmall)));
- ASSERT_EQ(0, writer.FinishEntry());
-
- ASSERT_EQ(0, writer.StartEntry("large.txt", 0));
- std::vector<uint8_t> data;
- data.resize(1024 * 1024, 0xef);
- ASSERT_EQ(0, writer.WriteBytes(data.data(), data.size()));
- ASSERT_EQ(0, writer.FinishEntry());
-
- off_t before_len = ftello(file_);
-
- ZipWriter::FileEntry entry;
- ASSERT_EQ(0, writer.GetLastEntry(&entry));
- ASSERT_EQ(0, writer.DiscardLastEntry());
-
- ASSERT_EQ(0, writer.Finish());
-
- off_t after_len = ftello(file_);
-
- ASSERT_GT(before_len, after_len);
-}
-
-static ::testing::AssertionResult AssertFileEntryContentsEq(const std::string& expected,
- ZipArchiveHandle handle,
- ZipEntry* zip_entry) {
- if (expected.size() != zip_entry->uncompressed_length) {
- return ::testing::AssertionFailure()
- << "uncompressed entry size " << zip_entry->uncompressed_length
- << " does not match expected size " << expected.size();
- }
-
- std::string actual;
- actual.resize(expected.size());
-
- uint8_t* buffer = reinterpret_cast<uint8_t*>(&*actual.begin());
- if (ExtractToMemory(handle, zip_entry, buffer, static_cast<uint32_t>(actual.size())) != 0) {
- return ::testing::AssertionFailure() << "failed to extract entry";
- }
-
- if (expected != actual) {
- return ::testing::AssertionFailure() << "actual zip_entry data '" << actual
- << "' does not match expected '" << expected << "'";
- }
- return ::testing::AssertionSuccess();
-}
diff --git a/libziparchive/ziptool-tests.xml b/libziparchive/ziptool-tests.xml
deleted file mode 100644
index 211119f..0000000
--- a/libziparchive/ziptool-tests.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<configuration description="Config for running ziptool-tests through Atest or in Infra">
- <option name="test-suite-tag" value="ziptool-tests" />
- <!-- This test requires a device, so it's not annotated with a null-device. -->
- <test class="com.android.tradefed.testtype.binary.ExecutableHostTest" >
- <option name="binary" value="run-ziptool-tests-on-android.sh" />
- <!-- Test script assumes a relative path with the cli-tests/ folders. -->
- <option name="relative-path-execution" value="true" />
- <!-- Tests shouldn't be that long but set 15m to be safe. -->
- <option name="per-binary-timeout" value="15m" />
- </test>
-</configuration>
diff --git a/libziparchive/ziptool.cpp b/libziparchive/ziptool.cpp
deleted file mode 100644
index dd42e90..0000000
--- a/libziparchive/ziptool.cpp
+++ /dev/null
@@ -1,528 +0,0 @@
-/*
- * 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.
- */
-
-#include <errno.h>
-#include <fcntl.h>
-#include <fnmatch.h>
-#include <getopt.h>
-#include <inttypes.h>
-#include <libgen.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <set>
-#include <string>
-
-#include <android-base/file.h>
-#include <android-base/strings.h>
-#include <ziparchive/zip_archive.h>
-
-using android::base::EndsWith;
-using android::base::StartsWith;
-
-enum OverwriteMode {
- kAlways,
- kNever,
- kPrompt,
-};
-
-enum Role {
- kUnzip,
- kZipinfo,
-};
-
-static Role role;
-static OverwriteMode overwrite_mode = kPrompt;
-static bool flag_1 = false;
-static std::string flag_d;
-static bool flag_l = false;
-static bool flag_p = false;
-static bool flag_q = false;
-static bool flag_v = false;
-static bool flag_x = false;
-static const char* archive_name = nullptr;
-static std::set<std::string> includes;
-static std::set<std::string> excludes;
-static uint64_t total_uncompressed_length = 0;
-static uint64_t total_compressed_length = 0;
-static size_t file_count = 0;
-
-static const char* g_progname;
-
-static void die(int error, const char* fmt, ...) {
- va_list ap;
-
- va_start(ap, fmt);
- fprintf(stderr, "%s: ", g_progname);
- vfprintf(stderr, fmt, ap);
- if (error != 0) fprintf(stderr, ": %s", strerror(error));
- fprintf(stderr, "\n");
- va_end(ap);
- exit(1);
-}
-
-static bool ShouldInclude(const std::string& name) {
- // Explicitly excluded?
- if (!excludes.empty()) {
- for (const auto& exclude : excludes) {
- if (!fnmatch(exclude.c_str(), name.c_str(), 0)) return false;
- }
- }
-
- // Implicitly included?
- if (includes.empty()) return true;
-
- // Explicitly included?
- for (const auto& include : includes) {
- if (!fnmatch(include.c_str(), name.c_str(), 0)) return true;
- }
- return false;
-}
-
-static bool MakeDirectoryHierarchy(const std::string& path) {
- // stat rather than lstat because a symbolic link to a directory is fine too.
- struct stat sb;
- if (stat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) return true;
-
- // Ensure the parent directories exist first.
- if (!MakeDirectoryHierarchy(android::base::Dirname(path))) return false;
-
- // Then try to create this directory.
- return (mkdir(path.c_str(), 0777) != -1);
-}
-
-static float CompressionRatio(int64_t uncompressed, int64_t compressed) {
- if (uncompressed == 0) return 0;
- return static_cast<float>(100LL * (uncompressed - compressed)) /
- static_cast<float>(uncompressed);
-}
-
-static void MaybeShowHeader(ZipArchiveHandle zah) {
- if (role == kUnzip) {
- // unzip has three formats.
- if (!flag_q) printf("Archive: %s\n", archive_name);
- if (flag_v) {
- printf(
- " Length Method Size Cmpr Date Time CRC-32 Name\n"
- "-------- ------ ------- ---- ---------- ----- -------- ----\n");
- } else if (flag_l) {
- printf(
- " Length Date Time Name\n"
- "--------- ---------- ----- ----\n");
- }
- } else {
- // zipinfo.
- if (!flag_1 && includes.empty() && excludes.empty()) {
- ZipArchiveInfo info{GetArchiveInfo(zah)};
- printf("Archive: %s\n", archive_name);
- printf("Zip file size: %" PRId64 " bytes, number of entries: %zu\n", info.archive_size,
- info.entry_count);
- }
- }
-}
-
-static void MaybeShowFooter() {
- if (role == kUnzip) {
- if (flag_v) {
- printf(
- "-------- ------- --- -------\n"
- "%8" PRId64 " %8" PRId64 " %3.0f%% %zu file%s\n",
- total_uncompressed_length, total_compressed_length,
- CompressionRatio(total_uncompressed_length, total_compressed_length), file_count,
- (file_count == 1) ? "" : "s");
- } else if (flag_l) {
- printf(
- "--------- -------\n"
- "%9" PRId64 " %zu file%s\n",
- total_uncompressed_length, file_count, (file_count == 1) ? "" : "s");
- }
- } else {
- if (!flag_1 && includes.empty() && excludes.empty()) {
- printf("%zu files, %" PRId64 " bytes uncompressed, %" PRId64 " bytes compressed: %.1f%%\n",
- file_count, total_uncompressed_length, total_compressed_length,
- CompressionRatio(total_uncompressed_length, total_compressed_length));
- }
- }
-}
-
-static bool PromptOverwrite(const std::string& dst) {
- // TODO: [r]ename not implemented because it doesn't seem useful.
- printf("replace %s? [y]es, [n]o, [A]ll, [N]one: ", dst.c_str());
- fflush(stdout);
- while (true) {
- char* line = nullptr;
- size_t n;
- if (getline(&line, &n, stdin) == -1) {
- die(0, "(EOF/read error; assuming [N]one...)");
- overwrite_mode = kNever;
- return false;
- }
- if (n == 0) continue;
- char cmd = line[0];
- free(line);
- switch (cmd) {
- case 'y':
- return true;
- case 'n':
- return false;
- case 'A':
- overwrite_mode = kAlways;
- return true;
- case 'N':
- overwrite_mode = kNever;
- return false;
- }
- }
-}
-
-static void ExtractToPipe(ZipArchiveHandle zah, ZipEntry& entry, const std::string& name) {
- // We need to extract to memory because ExtractEntryToFile insists on
- // being able to seek and truncate, and you can't do that with stdout.
- uint8_t* buffer = new uint8_t[entry.uncompressed_length];
- int err = ExtractToMemory(zah, &entry, buffer, entry.uncompressed_length);
- if (err < 0) {
- die(0, "failed to extract %s: %s", name.c_str(), ErrorCodeString(err));
- }
- if (!android::base::WriteFully(1, buffer, entry.uncompressed_length)) {
- die(errno, "failed to write %s to stdout", name.c_str());
- }
- delete[] buffer;
-}
-
-static void ExtractOne(ZipArchiveHandle zah, ZipEntry& entry, const std::string& name) {
- // Bad filename?
- if (StartsWith(name, "/") || StartsWith(name, "../") || name.find("/../") != std::string::npos) {
- die(0, "bad filename %s", name.c_str());
- }
-
- // Where are we actually extracting to (for human-readable output)?
- // flag_d is the empty string if -d wasn't used, or has a trailing '/'
- // otherwise.
- std::string dst = flag_d + name;
-
- // Ensure the directory hierarchy exists.
- if (!MakeDirectoryHierarchy(android::base::Dirname(name))) {
- die(errno, "couldn't create directory hierarchy for %s", dst.c_str());
- }
-
- // An entry in a zip file can just be a directory itself.
- if (EndsWith(name, "/")) {
- if (mkdir(name.c_str(), entry.unix_mode) == -1) {
- // If the directory already exists, that's fine.
- if (errno == EEXIST) {
- struct stat sb;
- if (stat(name.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) return;
- }
- die(errno, "couldn't extract directory %s", dst.c_str());
- }
- return;
- }
-
- // Create the file.
- int fd = open(name.c_str(), O_CREAT | O_WRONLY | O_CLOEXEC | O_EXCL, entry.unix_mode);
- if (fd == -1 && errno == EEXIST) {
- if (overwrite_mode == kNever) return;
- if (overwrite_mode == kPrompt && !PromptOverwrite(dst)) return;
- // Either overwrite_mode is kAlways or the user consented to this specific case.
- fd = open(name.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC | O_TRUNC, entry.unix_mode);
- }
- if (fd == -1) die(errno, "couldn't create file %s", dst.c_str());
-
- // Actually extract into the file.
- if (!flag_q) printf(" inflating: %s\n", dst.c_str());
- int err = ExtractEntryToFile(zah, &entry, fd);
- if (err < 0) die(0, "failed to extract %s: %s", dst.c_str(), ErrorCodeString(err));
- close(fd);
-}
-
-static void ListOne(const ZipEntry& entry, const std::string& name) {
- tm t = entry.GetModificationTime();
- char time[32];
- snprintf(time, sizeof(time), "%04d-%02d-%02d %02d:%02d", t.tm_year + 1900, t.tm_mon + 1,
- t.tm_mday, t.tm_hour, t.tm_min);
- if (flag_v) {
- printf("%8d %s %7d %3.0f%% %s %08x %s\n", entry.uncompressed_length,
- (entry.method == kCompressStored) ? "Stored" : "Defl:N", entry.compressed_length,
- CompressionRatio(entry.uncompressed_length, entry.compressed_length), time, entry.crc32,
- name.c_str());
- } else {
- printf("%9d %s %s\n", entry.uncompressed_length, time, name.c_str());
- }
-}
-
-static void InfoOne(const ZipEntry& entry, const std::string& name) {
- if (flag_1) {
- // "android-ndk-r19b/sources/android/NOTICE"
- printf("%s\n", name.c_str());
- return;
- }
-
- int version = entry.version_made_by & 0xff;
- int os = (entry.version_made_by >> 8) & 0xff;
-
- // TODO: Support suid/sgid? Non-Unix/non-FAT host file system attributes?
- const char* src_fs = "???";
- char mode[] = "??? ";
- if (os == 0) {
- src_fs = "fat";
- // https://docs.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
- int attrs = entry.external_file_attributes & 0xff;
- mode[0] = (attrs & 0x10) ? 'd' : '-';
- mode[1] = 'r';
- mode[2] = (attrs & 0x01) ? '-' : 'w';
- // The man page also mentions ".btm", but that seems to be obsolete?
- mode[3] = EndsWith(name, ".exe") || EndsWith(name, ".com") || EndsWith(name, ".bat") ||
- EndsWith(name, ".cmd")
- ? 'x'
- : '-';
- mode[4] = (attrs & 0x20) ? 'a' : '-';
- mode[5] = (attrs & 0x02) ? 'h' : '-';
- mode[6] = (attrs & 0x04) ? 's' : '-';
- } else if (os == 3) {
- src_fs = "unx";
- mode[0] = S_ISDIR(entry.unix_mode) ? 'd' : (S_ISREG(entry.unix_mode) ? '-' : '?');
- mode[1] = entry.unix_mode & S_IRUSR ? 'r' : '-';
- mode[2] = entry.unix_mode & S_IWUSR ? 'w' : '-';
- mode[3] = entry.unix_mode & S_IXUSR ? 'x' : '-';
- mode[4] = entry.unix_mode & S_IRGRP ? 'r' : '-';
- mode[5] = entry.unix_mode & S_IWGRP ? 'w' : '-';
- mode[6] = entry.unix_mode & S_IXGRP ? 'x' : '-';
- mode[7] = entry.unix_mode & S_IROTH ? 'r' : '-';
- mode[8] = entry.unix_mode & S_IWOTH ? 'w' : '-';
- mode[9] = entry.unix_mode & S_IXOTH ? 'x' : '-';
- }
-
- char method[5] = "stor";
- if (entry.method == kCompressDeflated) {
- snprintf(method, sizeof(method), "def%c", "NXFS"[(entry.gpbf >> 1) & 0x3]);
- }
-
- // TODO: zipinfo (unlike unzip) sometimes uses time zone?
- // TODO: this uses 4-digit years because we're not barbarians unless interoperability forces it.
- tm t = entry.GetModificationTime();
- char time[32];
- snprintf(time, sizeof(time), "%04d-%02d-%02d %02d:%02d", t.tm_year + 1900, t.tm_mon + 1,
- t.tm_mday, t.tm_hour, t.tm_min);
-
- // "-rw-r--r-- 3.0 unx 577 t- defX 19-Feb-12 16:09 android-ndk-r19b/sources/android/NOTICE"
- printf("%s %2d.%d %s %8d %c%c %s %s %s\n", mode, version / 10, version % 10, src_fs,
- entry.uncompressed_length, entry.is_text ? 't' : 'b',
- entry.has_data_descriptor ? 'X' : 'x', method, time, name.c_str());
-}
-
-static void ProcessOne(ZipArchiveHandle zah, ZipEntry& entry, const std::string& name) {
- if (role == kUnzip) {
- if (flag_l || flag_v) {
- // -l or -lv or -lq or -v.
- ListOne(entry, name);
- } else {
- // Actually extract.
- if (flag_p) {
- ExtractToPipe(zah, entry, name);
- } else {
- ExtractOne(zah, entry, name);
- }
- }
- } else {
- // zipinfo or zipinfo -1.
- InfoOne(entry, name);
- }
- total_uncompressed_length += entry.uncompressed_length;
- total_compressed_length += entry.compressed_length;
- ++file_count;
-}
-
-static void ProcessAll(ZipArchiveHandle zah) {
- MaybeShowHeader(zah);
-
- // libziparchive iteration order doesn't match the central directory.
- // We could sort, but that would cost extra and wouldn't match either.
- void* cookie;
- int err = StartIteration(zah, &cookie);
- if (err != 0) {
- die(0, "couldn't iterate %s: %s", archive_name, ErrorCodeString(err));
- }
-
- ZipEntry entry;
- std::string name;
- while ((err = Next(cookie, &entry, &name)) >= 0) {
- if (ShouldInclude(name)) ProcessOne(zah, entry, name);
- }
-
- if (err < -1) die(0, "failed iterating %s: %s", archive_name, ErrorCodeString(err));
- EndIteration(cookie);
-
- MaybeShowFooter();
-}
-
-static void ShowHelp(bool full) {
- if (role == kUnzip) {
- fprintf(full ? stdout : stderr, "usage: unzip [-d DIR] [-lnopqv] ZIP [FILE...] [-x FILE...]\n");
- if (!full) exit(EXIT_FAILURE);
-
- printf(
- "\n"
- "Extract FILEs from ZIP archive. Default is all files. Both the include and\n"
- "exclude (-x) lists use shell glob patterns.\n"
- "\n"
- "-d DIR Extract into DIR\n"
- "-l List contents (-lq excludes archive name, -lv is verbose)\n"
- "-n Never overwrite files (default: prompt)\n"
- "-o Always overwrite files\n"
- "-p Pipe to stdout\n"
- "-q Quiet\n"
- "-v List contents verbosely\n"
- "-x FILE Exclude files\n");
- } else {
- fprintf(full ? stdout : stderr, "usage: zipinfo [-1] ZIP [FILE...] [-x FILE...]\n");
- if (!full) exit(EXIT_FAILURE);
-
- printf(
- "\n"
- "Show information about FILEs from ZIP archive. Default is all files.\n"
- "Both the include and exclude (-x) lists use shell glob patterns.\n"
- "\n"
- "-1 Show filenames only, one per line\n"
- "-x FILE Exclude files\n");
- }
- exit(EXIT_SUCCESS);
-}
-
-static void HandleCommonOption(int opt) {
- switch (opt) {
- case 'h':
- ShowHelp(true);
- break;
- case 'x':
- flag_x = true;
- break;
- case 1:
- // -x swallows all following arguments, so we use '-' in the getopt
- // string and collect files here.
- if (!archive_name) {
- archive_name = optarg;
- } else if (flag_x) {
- excludes.insert(optarg);
- } else {
- includes.insert(optarg);
- }
- break;
- default:
- ShowHelp(false);
- break;
- }
-}
-
-int main(int argc, char* argv[]) {
- // Who am I, and what am I doing?
- g_progname = basename(argv[0]);
- if (!strcmp(g_progname, "ziptool") && argc > 1) return main(argc - 1, argv + 1);
- if (!strcmp(g_progname, "unzip")) {
- role = kUnzip;
- } else if (!strcmp(g_progname, "zipinfo")) {
- role = kZipinfo;
- } else {
- die(0, "run as ziptool with unzip or zipinfo as the first argument, or symlink");
- }
-
- static const struct option opts[] = {
- {"help", no_argument, 0, 'h'},
- {},
- };
-
- if (role == kUnzip) {
- // `unzip -Z` is "zipinfo mode", so in that case just restart...
- if (argc > 1 && !strcmp(argv[1], "-Z")) {
- argv[1] = const_cast<char*>("zipinfo");
- return main(argc - 1, argv + 1);
- }
-
- int opt;
- while ((opt = getopt_long(argc, argv, "-d:hlnopqvx", opts, nullptr)) != -1) {
- switch (opt) {
- case 'd':
- flag_d = optarg;
- if (!EndsWith(flag_d, "/")) flag_d += '/';
- break;
- case 'l':
- flag_l = true;
- break;
- case 'n':
- overwrite_mode = kNever;
- break;
- case 'o':
- overwrite_mode = kAlways;
- break;
- case 'p':
- flag_p = flag_q = true;
- break;
- case 'q':
- flag_q = true;
- break;
- case 'v':
- flag_v = true;
- break;
- default:
- HandleCommonOption(opt);
- break;
- }
- }
- } else {
- int opt;
- while ((opt = getopt_long(argc, argv, "-1hx", opts, nullptr)) != -1) {
- switch (opt) {
- case '1':
- flag_1 = true;
- break;
- default:
- HandleCommonOption(opt);
- break;
- }
- }
- }
-
- if (!archive_name) die(0, "missing archive filename");
-
- // We can't support "-" to unzip from stdin because libziparchive relies on mmap.
- ZipArchiveHandle zah;
- int32_t err;
- if ((err = OpenArchive(archive_name, &zah)) != 0) {
- die(0, "couldn't open %s: %s", archive_name, ErrorCodeString(err));
- }
-
- // Implement -d by changing into that directory.
- // We'll create implicit directories based on paths in the zip file, and we'll create
- // the -d directory itself, but we require that *parents* of the -d directory already exists.
- // This is pretty arbitrary, but it's the behavior of the original unzip.
- if (!flag_d.empty()) {
- if (mkdir(flag_d.c_str(), 0777) == -1 && errno != EEXIST) {
- die(errno, "couldn't created %s", flag_d.c_str());
- }
- if (chdir(flag_d.c_str()) == -1) {
- die(errno, "couldn't chdir to %s", flag_d.c_str());
- }
- }
-
- ProcessAll(zah);
-
- CloseArchive(zah);
- return 0;
-}
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/event.logtags b/logcat/event.logtags
index 3a1d36f..56a670a 100644
--- a/logcat/event.logtags
+++ b/logcat/event.logtags
@@ -145,6 +145,8 @@
# libcore failure logging
90100 exp_det_cert_pin_failure (certs|4)
+# 150000 - 160000 reserved for Android Automotive builds
+
1397638484 snet_event_log (subtag|3) (uid|1) (message|3)
# for events that go to stats log buffer
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 76a970f..0056c80 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -36,6 +36,7 @@
#include <memory>
#include <regex>
+#include <set>
#include <string>
#include <utility>
#include <vector>
@@ -50,6 +51,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 +124,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 +166,8 @@
break;
}
+ closeLogFile(file0.c_str());
+
int err = rename(file0.c_str(), file1.c_str());
if (err < 0 && errno != ENOENT) {
@@ -343,6 +359,10 @@
-T '<time>' Print the lines since specified time (not imply -d).
count is pure numerical, time is 'MM-DD hh:mm:ss.mmm...'
'YYYY-MM-DD hh:mm:ss.mmm...' or 'sssss.mmm...' format.
+ --uid=<uids> Only display log messages from UIDs present in the comma separate list
+ <uids>. No name look-up is performed, so UIDs must be provided as
+ numeric values. This option is only useful for the 'root', 'log', and
+ 'system' users since only those users can view logs from other users.
)init");
fprintf(stderr, "\nfilterspecs are a series of \n"
@@ -444,7 +464,7 @@
closedir);
if (!dir.get()) return retval;
- log_time now(android_log_clockid());
+ log_time now(CLOCK_REALTIME);
size_t len = strlen(file);
log_time modulo(0, NS_PER_SEC);
@@ -513,13 +533,14 @@
unsigned long setLogSize = 0;
const char* setPruneList = nullptr;
const char* setId = nullptr;
- int mode = ANDROID_LOG_RDONLY;
+ int mode = 0;
std::string forceFilters;
size_t tail_lines = 0;
log_time tail_time(log_time::EPOCH);
size_t pid = 0;
bool got_t = false;
unsigned id_mask = 0;
+ std::set<uid_t> uids;
if (argc == 2 && !strcmp(argv[1], "--help")) {
show_help();
@@ -539,6 +560,7 @@
static const char id_str[] = "id";
static const char wrap_str[] = "wrap";
static const char print_str[] = "print";
+ static const char uid_str[] = "uid";
// clang-format off
static const struct option long_options[] = {
{ "binary", no_argument, nullptr, 'B' },
@@ -566,6 +588,7 @@
{ "statistics", no_argument, nullptr, 'S' },
// hidden and undocumented reserved alias for -t
{ "tail", required_argument, nullptr, 't' },
+ { uid_str, required_argument, nullptr, 0 },
// support, but ignore and do not document, the optional argument
{ wrap_str, optional_argument, nullptr, 0 },
{ nullptr, 0, nullptr, 0 }
@@ -591,8 +614,7 @@
break;
}
if (long_options[option_index].name == wrap_str) {
- mode |= ANDROID_LOG_WRAP | ANDROID_LOG_RDONLY |
- ANDROID_LOG_NONBLOCK;
+ mode |= ANDROID_LOG_WRAP | ANDROID_LOG_NONBLOCK;
// ToDo: implement API that supports setting a wrap timeout
size_t dummy = ANDROID_LOG_WRAP_DEFAULT_TIMEOUT;
if (optarg && (!ParseUint(optarg, &dummy) || dummy < 1)) {
@@ -617,6 +639,17 @@
if (long_options[option_index].name == id_str) {
setId = (optarg && optarg[0]) ? optarg : nullptr;
}
+ if (long_options[option_index].name == uid_str) {
+ auto uid_strings = Split(optarg, delimiters);
+ for (const auto& uid_string : uid_strings) {
+ uid_t uid;
+ if (!ParseUint(uid_string, &uid)) {
+ error(EXIT_FAILURE, 0, "Unable to parse UID '%s'", uid_string.c_str());
+ }
+ uids.emplace(uid);
+ }
+ break;
+ }
break;
case 's':
@@ -626,21 +659,19 @@
case 'c':
clearLog = true;
- mode |= ANDROID_LOG_WRONLY;
break;
case 'L':
- mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_PSTORE |
- ANDROID_LOG_NONBLOCK;
+ mode |= ANDROID_LOG_PSTORE | ANDROID_LOG_NONBLOCK;
break;
case 'd':
- mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
+ mode |= ANDROID_LOG_NONBLOCK;
break;
case 't':
got_t = true;
- mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
+ mode |= ANDROID_LOG_NONBLOCK;
FALLTHROUGH_INTENDED;
case 'T':
if (strspn(optarg, "0123456789") != strlen(optarg)) {
@@ -1152,6 +1183,10 @@
LOG_ID_MAX);
}
+ if (!uids.empty() && uids.count(log_msg.entry.uid) == 0) {
+ continue;
+ }
+
PrintDividers(log_msg.id(), printDividers);
if (print_binary_) {
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index b32b437..a2daeb0 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -16,6 +16,7 @@
#include <ctype.h>
#include <dirent.h>
+#include <pwd.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
@@ -28,10 +29,12 @@
#include <unistd.h>
#include <memory>
+#include <regex>
#include <string>
#include <android-base/file.h>
#include <android-base/macros.h>
+#include <android-base/parseint.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <gtest/gtest.h>
@@ -174,11 +177,6 @@
}
TEST(logcat, year) {
- if (android_log_clockid() == CLOCK_MONOTONIC) {
- fprintf(stderr, "Skipping test, logd is monotonic time\n");
- return;
- }
-
int count;
int tries = 3; // in case run too soon after system start or buffer clear
@@ -249,11 +247,6 @@
}
TEST(logcat, tz) {
- if (android_log_clockid() == CLOCK_MONOTONIC) {
- fprintf(stderr, "Skipping test, logd is monotonic time\n");
- return;
- }
-
int tries = 4; // in case run too soon after system start or buffer clear
int count;
@@ -485,8 +478,8 @@
continue;
}
- log_time tx((const char*)&t);
- if (ts == tx) {
+ log_time* tx = reinterpret_cast<log_time*>(&t);
+ if (ts == *tx) {
++count;
}
}
@@ -531,8 +524,8 @@
continue;
}
- log_time tx((const char*)&t);
- if (ts == tx) {
+ log_time* tx = reinterpret_cast<log_time*>(&t);
+ if (ts == *tx) {
++count;
}
}
@@ -1758,3 +1751,83 @@
ASSERT_TRUE(android::base::StartsWith(output, "unknown buffer foo\n"));
}
+
+static void SniffUid(const std::string& line, uid_t& uid) {
+ auto uid_regex = std::regex{"\\S+\\s+\\S+\\s+(\\S+).*"};
+
+ auto trimmed_line = android::base::Trim(line);
+
+ std::smatch match_results;
+ ASSERT_TRUE(std::regex_match(trimmed_line, match_results, uid_regex))
+ << "Unable to find UID in line '" << trimmed_line << "'";
+ auto uid_string = match_results[1];
+ if (!android::base::ParseUint(uid_string, &uid)) {
+ auto pwd = getpwnam(uid_string.str().c_str());
+ ASSERT_NE(nullptr, pwd) << "uid '" << uid_string << "' in line '" << trimmed_line << "'";
+ uid = pwd->pw_uid;
+ }
+}
+
+static void UidsInLog(std::optional<std::vector<uid_t>> filter_uid, std::map<uid_t, size_t>& uids) {
+ std::string command;
+ if (filter_uid) {
+ std::vector<std::string> uid_strings;
+ for (const auto& uid : *filter_uid) {
+ uid_strings.emplace_back(std::to_string(uid));
+ }
+ command = android::base::StringPrintf(logcat_executable
+ " -v uid -b all -d 2>/dev/null --uid=%s",
+ android::base::Join(uid_strings, ",").c_str());
+ } else {
+ command = logcat_executable " -v uid -b all -d 2>/dev/null";
+ }
+ auto fp = std::unique_ptr<FILE, decltype(&pclose)>(popen(command.c_str(), "r"), pclose);
+ ASSERT_NE(nullptr, fp);
+
+ char buffer[BIG_BUFFER];
+ while (fgets(buffer, sizeof(buffer), fp.get())) {
+ // Ignore dividers, e.g. '--------- beginning of radio'
+ if (android::base::StartsWith(buffer, "---------")) {
+ continue;
+ }
+ uid_t uid;
+ SniffUid(buffer, uid);
+ uids[uid]++;
+ }
+}
+
+static std::vector<uid_t> TopTwoInMap(const std::map<uid_t, size_t>& uids) {
+ std::pair<uid_t, size_t> top = {0, 0};
+ std::pair<uid_t, size_t> second = {0, 0};
+ for (const auto& [uid, count] : uids) {
+ if (count > top.second) {
+ top = second;
+ top = {uid, count};
+ } else if (count > second.second) {
+ second = {uid, count};
+ }
+ }
+ return {top.first, second.first};
+}
+
+TEST(logcat, uid_filter) {
+ std::map<uid_t, size_t> uids;
+ UidsInLog({}, uids);
+
+ ASSERT_GT(uids.size(), 2U);
+ auto top_uids = TopTwoInMap(uids);
+
+ // Test filtering with --uid=<top uid>
+ std::map<uid_t, size_t> uids_only_top;
+ std::vector<uid_t> top_uid = {top_uids[0]};
+ UidsInLog(top_uid, uids_only_top);
+
+ EXPECT_EQ(1U, uids_only_top.size());
+
+ // Test filtering with --uid=<top uid>,<2nd top uid>
+ std::map<uid_t, size_t> uids_only_top2;
+ std::vector<uid_t> top2_uids = {top_uids[0], top_uids[1]};
+ UidsInLog(top2_uids, uids_only_top2);
+
+ EXPECT_EQ(2U, uids_only_top2.size());
+}
diff --git a/logd/Android.bp b/logd/Android.bp
index b337b7c..1f6ab34 100644
--- a/logd/Android.bp
+++ b/logd/Android.bp
@@ -28,39 +28,54 @@
"-DLIBLOG_LOG_TAG=1006",
]
+cc_defaults {
+ name: "logd_defaults",
+
+ shared_libs: ["libbase"],
+ cflags: [
+ "-Wextra",
+ "-Wthread-safety",
+ ] + event_flag,
+
+ lto: {
+ thin: true,
+ },
+}
+
cc_library_static {
name: "liblogd",
-
+ defaults: ["logd_defaults"],
+ host_supported: true,
srcs: [
- "LogCommand.cpp",
- "CommandListener.cpp",
- "LogListener.cpp",
- "LogReader.cpp",
- "FlushCommand.cpp",
- "LogBuffer.cpp",
+ "ChattyLogBuffer.cpp",
+ "LogReaderList.cpp",
+ "LogReaderThread.cpp",
"LogBufferElement.cpp",
- "LogTimes.cpp",
"LogStatistics.cpp",
"LogWhiteBlackList.cpp",
- "libaudit.c",
- "LogAudit.cpp",
- "LogKlog.cpp",
"LogTags.cpp",
+ "SimpleLogBuffer.cpp",
],
logtags: ["event.logtags"],
- shared_libs: ["libbase"],
-
export_include_dirs: ["."],
-
- cflags: ["-Werror"] + event_flag,
}
cc_binary {
name: "logd",
+ defaults: ["logd_defaults"],
init_rc: ["logd.rc"],
- srcs: ["main.cpp"],
+ srcs: [
+ "main.cpp",
+ "LogPermissions.cpp",
+ "CommandListener.cpp",
+ "LogListener.cpp",
+ "LogReader.cpp",
+ "LogAudit.cpp",
+ "LogKlog.cpp",
+ "libaudit.cpp",
+ ],
static_libs: [
"liblog",
@@ -70,31 +85,24 @@
shared_libs: [
"libsysutils",
"libcutils",
- "libbase",
"libpackagelistparser",
"libprocessgroup",
"libcap",
],
-
- cflags: ["-Werror"],
}
cc_binary {
name: "auditctl",
- srcs: ["auditctl.cpp"],
-
- static_libs: [
- "liblogd",
+ srcs: [
+ "auditctl.cpp",
+ "libaudit.cpp",
],
shared_libs: ["libbase"],
cflags: [
- "-Wall",
"-Wextra",
- "-Werror",
- "-Wconversion"
],
}
@@ -103,3 +111,59 @@
src: "logtagd.rc",
sub_dir: "init",
}
+
+// -----------------------------------------------------------------------------
+// Unit tests.
+// -----------------------------------------------------------------------------
+
+cc_defaults {
+ name: "logd-unit-test-defaults",
+
+ cflags: [
+ "-fstack-protector-all",
+ "-g",
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-fno-builtin",
+ ] + event_flag,
+
+ srcs: [
+ "ChattyLogBufferTest.cpp",
+ "logd_test.cpp",
+ "LogBufferTest.cpp",
+ ],
+
+ static_libs: [
+ "libbase",
+ "libcutils",
+ "liblog",
+ "liblogd",
+ "libselinux",
+ ],
+}
+
+// Build tests for the logger. Run with:
+// adb shell /data/nativetest/logd-unit-tests/logd-unit-tests
+cc_test {
+ name: "logd-unit-tests",
+ host_supported: true,
+ defaults: ["logd-unit-test-defaults"],
+}
+
+cc_test {
+ name: "CtsLogdTestCases",
+ defaults: ["logd-unit-test-defaults"],
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
+ test_suites: [
+ "cts",
+ "vts10",
+ ],
+}
diff --git a/logd/tests/AndroidTest.xml b/logd/AndroidTest.xml
similarity index 100%
rename from logd/tests/AndroidTest.xml
rename to logd/AndroidTest.xml
diff --git a/logd/ChattyLogBuffer.cpp b/logd/ChattyLogBuffer.cpp
new file mode 100644
index 0000000..f92fe65
--- /dev/null
+++ b/logd/ChattyLogBuffer.cpp
@@ -0,0 +1,619 @@
+/*
+ * Copyright (C) 2012-2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// for manual checking of stale entries during ChattyLogBuffer::erase()
+//#define DEBUG_CHECK_FOR_STALE_ENTRIES
+
+#include "ChattyLogBuffer.h"
+
+#include <ctype.h>
+#include <endian.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/cdefs.h>
+#include <sys/user.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <limits>
+#include <unordered_map>
+#include <utility>
+
+#include <private/android_logger.h>
+
+#include "LogUtils.h"
+
+#ifndef __predict_false
+#define __predict_false(exp) __builtin_expect((exp) != 0, 0)
+#endif
+
+ChattyLogBuffer::ChattyLogBuffer(LogReaderList* reader_list, LogTags* tags, PruneList* prune,
+ LogStatistics* stats)
+ : SimpleLogBuffer(reader_list, tags, stats), prune_(prune) {}
+
+ChattyLogBuffer::~ChattyLogBuffer() {}
+
+enum match_type { DIFFERENT, SAME, SAME_LIBLOG };
+
+static enum match_type Identical(const LogBufferElement& elem, const LogBufferElement& last) {
+ ssize_t lenl = elem.msg_len();
+ if (lenl <= 0) return DIFFERENT; // value if this represents a chatty elem
+ ssize_t lenr = last.msg_len();
+ if (lenr <= 0) return DIFFERENT; // value if this represents a chatty elem
+ if (elem.uid() != last.uid()) return DIFFERENT;
+ if (elem.pid() != last.pid()) return DIFFERENT;
+ if (elem.tid() != last.tid()) return DIFFERENT;
+
+ // last is more than a minute old, stop squashing identical messages
+ if (elem.realtime().nsec() > (last.realtime().nsec() + 60 * NS_PER_SEC)) return DIFFERENT;
+
+ // Identical message
+ const char* msgl = elem.msg();
+ const char* msgr = last.msg();
+ if (lenl == lenr) {
+ if (!fastcmp<memcmp>(msgl, msgr, lenl)) return SAME;
+ // liblog tagged messages (content gets summed)
+ if (elem.log_id() == LOG_ID_EVENTS && lenl == sizeof(android_log_event_int_t) &&
+ !fastcmp<memcmp>(msgl, msgr, sizeof(android_log_event_int_t) - sizeof(int32_t)) &&
+ elem.GetTag() == LIBLOG_LOG_TAG) {
+ return SAME_LIBLOG;
+ }
+ }
+
+ // audit message (except sequence number) identical?
+ if (IsBinary(last.log_id()) &&
+ lenl > static_cast<ssize_t>(sizeof(android_log_event_string_t)) &&
+ lenr > static_cast<ssize_t>(sizeof(android_log_event_string_t))) {
+ if (fastcmp<memcmp>(msgl, msgr, sizeof(android_log_event_string_t) - sizeof(int32_t))) {
+ return DIFFERENT;
+ }
+ msgl += sizeof(android_log_event_string_t);
+ lenl -= sizeof(android_log_event_string_t);
+ msgr += sizeof(android_log_event_string_t);
+ lenr -= sizeof(android_log_event_string_t);
+ }
+ static const char avc[] = "): avc: ";
+ const char* avcl = android::strnstr(msgl, lenl, avc);
+ if (!avcl) return DIFFERENT;
+ lenl -= avcl - msgl;
+ const char* avcr = android::strnstr(msgr, lenr, avc);
+ if (!avcr) return DIFFERENT;
+ lenr -= avcr - msgr;
+ if (lenl != lenr) return DIFFERENT;
+ if (fastcmp<memcmp>(avcl + strlen(avc), avcr + strlen(avc), lenl - strlen(avc))) {
+ return DIFFERENT;
+ }
+ return SAME;
+}
+
+void ChattyLogBuffer::LogInternal(LogBufferElement&& elem) {
+ // b/137093665: don't coalesce security messages.
+ if (elem.log_id() == LOG_ID_SECURITY) {
+ SimpleLogBuffer::LogInternal(std::move(elem));
+ return;
+ }
+ int log_id = elem.log_id();
+
+ // Initialize last_logged_elements_ to a copy of elem if logging the first element for a log_id.
+ if (!last_logged_elements_[log_id]) {
+ last_logged_elements_[log_id].emplace(elem);
+ SimpleLogBuffer::LogInternal(std::move(elem));
+ return;
+ }
+
+ LogBufferElement& current_last = *last_logged_elements_[log_id];
+ enum match_type match = Identical(elem, current_last);
+
+ if (match == DIFFERENT) {
+ if (duplicate_elements_[log_id]) {
+ // If we previously had 3+ identical messages, log the chatty message.
+ if (duplicate_elements_[log_id]->dropped_count() > 0) {
+ SimpleLogBuffer::LogInternal(std::move(*duplicate_elements_[log_id]));
+ }
+ duplicate_elements_[log_id].reset();
+ // Log the saved copy of the last identical message seen.
+ SimpleLogBuffer::LogInternal(std::move(current_last));
+ }
+ last_logged_elements_[log_id].emplace(elem);
+ SimpleLogBuffer::LogInternal(std::move(elem));
+ return;
+ }
+
+ // 2 identical message: set duplicate_elements_ appropriately.
+ if (!duplicate_elements_[log_id]) {
+ duplicate_elements_[log_id].emplace(std::move(current_last));
+ last_logged_elements_[log_id].emplace(std::move(elem));
+ return;
+ }
+
+ // 3+ identical LIBLOG event messages: coalesce them into last_logged_elements_.
+ if (match == SAME_LIBLOG) {
+ const android_log_event_int_t* current_last_event =
+ reinterpret_cast<const android_log_event_int_t*>(current_last.msg());
+ int64_t current_last_count = current_last_event->payload.data;
+ android_log_event_int_t* elem_event =
+ reinterpret_cast<android_log_event_int_t*>(const_cast<char*>(elem.msg()));
+ int64_t elem_count = elem_event->payload.data;
+
+ int64_t total = current_last_count + elem_count;
+ if (total > std::numeric_limits<int32_t>::max()) {
+ SimpleLogBuffer::LogInternal(std::move(current_last));
+ last_logged_elements_[log_id].emplace(std::move(elem));
+ return;
+ }
+ stats()->AddTotal(current_last.log_id(), current_last.msg_len());
+ elem_event->payload.data = total;
+ last_logged_elements_[log_id].emplace(std::move(elem));
+ return;
+ }
+
+ // 3+ identical messages (not LIBLOG) messages: increase the drop count.
+ uint16_t dropped_count = duplicate_elements_[log_id]->dropped_count();
+ if (dropped_count == std::numeric_limits<uint16_t>::max()) {
+ SimpleLogBuffer::LogInternal(std::move(*duplicate_elements_[log_id]));
+ dropped_count = 0;
+ }
+ // We're dropping the current_last log so add its stats to the total.
+ stats()->AddTotal(current_last.log_id(), current_last.msg_len());
+ // Use current_last for tracking the dropped count to always use the latest timestamp.
+ current_last.SetDropped(dropped_count + 1);
+ duplicate_elements_[log_id].emplace(std::move(current_last));
+ last_logged_elements_[log_id].emplace(std::move(elem));
+}
+
+LogBufferElementCollection::iterator ChattyLogBuffer::Erase(LogBufferElementCollection::iterator it,
+ bool coalesce) {
+ LogBufferElement& element = *it;
+ log_id_t id = element.log_id();
+
+ // Remove iterator references in the various lists that will become stale
+ // after the element is erased from the main logging list.
+
+ { // start of scope for found iterator
+ int key = (id == LOG_ID_EVENTS || id == LOG_ID_SECURITY) ? element.GetTag() : element.uid();
+ LogBufferIteratorMap::iterator found = mLastWorst[id].find(key);
+ if ((found != mLastWorst[id].end()) && (it == found->second)) {
+ mLastWorst[id].erase(found);
+ }
+ }
+
+ { // start of scope for pid found iterator
+ // element->uid() may not be AID_SYSTEM for next-best-watermark.
+ // will not assume id != LOG_ID_EVENTS or LOG_ID_SECURITY for KISS and
+ // long term code stability, find() check should be fast for those ids.
+ LogBufferPidIteratorMap::iterator found = mLastWorstPidOfSystem[id].find(element.pid());
+ if (found != mLastWorstPidOfSystem[id].end() && it == found->second) {
+ mLastWorstPidOfSystem[id].erase(found);
+ }
+ }
+
+#ifdef DEBUG_CHECK_FOR_STALE_ENTRIES
+ LogBufferElementCollection::iterator bad = it;
+ int key = (id == LOG_ID_EVENTS || id == LOG_ID_SECURITY) ? element->GetTag() : element->uid();
+#endif
+
+ if (coalesce) {
+ stats()->Erase(element.ToLogStatisticsElement());
+ } else {
+ stats()->Subtract(element.ToLogStatisticsElement());
+ }
+
+ it = SimpleLogBuffer::Erase(it);
+
+#ifdef DEBUG_CHECK_FOR_STALE_ENTRIES
+ log_id_for_each(i) {
+ for (auto b : mLastWorst[i]) {
+ if (bad == b.second) {
+ LOG(ERROR) << StringPrintf("stale mLastWorst[%d] key=%d mykey=%d", i, b.first, key);
+ }
+ }
+ for (auto b : mLastWorstPidOfSystem[i]) {
+ if (bad == b.second) {
+ LOG(ERROR) << StringPrintf("stale mLastWorstPidOfSystem[%d] pid=%d", i, b.first);
+ }
+ }
+ }
+#endif
+ return it;
+}
+
+// Define a temporary mechanism to report the last LogBufferElement pointer
+// for the specified uid, pid and tid. Used below to help merge-sort when
+// pruning for worst UID.
+class LogBufferElementLast {
+ typedef std::unordered_map<uint64_t, LogBufferElement*> LogBufferElementMap;
+ LogBufferElementMap map;
+
+ public:
+ bool coalesce(LogBufferElement* element, uint16_t dropped) {
+ uint64_t key = LogBufferElementKey(element->uid(), element->pid(), element->tid());
+ LogBufferElementMap::iterator it = map.find(key);
+ if (it != map.end()) {
+ LogBufferElement* found = it->second;
+ uint16_t moreDropped = found->dropped_count();
+ if ((dropped + moreDropped) > USHRT_MAX) {
+ map.erase(it);
+ } else {
+ found->SetDropped(dropped + moreDropped);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void add(LogBufferElement* element) {
+ uint64_t key = LogBufferElementKey(element->uid(), element->pid(), element->tid());
+ map[key] = element;
+ }
+
+ void clear() { map.clear(); }
+
+ void clear(LogBufferElement* element) {
+ uint64_t current = element->realtime().nsec() - (EXPIRE_RATELIMIT * NS_PER_SEC);
+ for (LogBufferElementMap::iterator it = map.begin(); it != map.end();) {
+ LogBufferElement* mapElement = it->second;
+ if (mapElement->dropped_count() >= EXPIRE_THRESHOLD &&
+ current > mapElement->realtime().nsec()) {
+ it = map.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+
+ private:
+ uint64_t LogBufferElementKey(uid_t uid, pid_t pid, pid_t tid) {
+ return uint64_t(uid) << 32 | uint64_t(pid) << 16 | uint64_t(tid);
+ }
+};
+
+// prune "pruneRows" of type "id" from the buffer.
+//
+// This garbage collection task is used to expire log entries. It is called to
+// remove all logs (clear), all UID logs (unprivileged clear), or every
+// 256 or 10% of the total logs (whichever is less) to prune the logs.
+//
+// First there is a prep phase where we discover the reader region lock that
+// acts as a backstop to any pruning activity to stop there and go no further.
+//
+// There are three major pruning loops that follow. All expire from the oldest
+// entries. Since there are multiple log buffers, the Android logging facility
+// will appear to drop entries 'in the middle' when looking at multiple log
+// sources and buffers. This effect is slightly more prominent when we prune
+// the worst offender by logging source. Thus the logs slowly loose content
+// and value as you move back in time. This is preferred since chatty sources
+// invariably move the logs value down faster as less chatty sources would be
+// expired in the noise.
+//
+// The first loop performs blacklisting and worst offender pruning. Falling
+// through when there are no notable worst offenders and have not hit the
+// region lock preventing further worst offender pruning. This loop also looks
+// after managing the chatty log entries and merging to help provide
+// statistical basis for blame. The chatty entries are not a notification of
+// how much logs you may have, but instead represent how much logs you would
+// have had in a virtual log buffer that is extended to cover all the in-memory
+// logs without loss. They last much longer than the represented pruned logs
+// since they get multiplied by the gains in the non-chatty log sources.
+//
+// The second loop get complicated because an algorithm of watermarks and
+// history is maintained to reduce the order and keep processing time
+// down to a minimum at scale. These algorithms can be costly in the face
+// of larger log buffers, or severly limited processing time granted to a
+// background task at lowest priority.
+//
+// This second loop does straight-up expiration from the end of the logs
+// (again, remember for the specified log buffer id) but does some whitelist
+// preservation. Thus whitelist is a Hail Mary low priority, blacklists and
+// spam filtration all take priority. This second loop also checks if a region
+// lock is causing us to buffer too much in the logs to help the reader(s),
+// and will tell the slowest reader thread to skip log entries, and if
+// persistent and hits a further threshold, kill the reader thread.
+//
+// The third thread is optional, and only gets hit if there was a whitelist
+// and more needs to be pruned against the backstop of the region lock.
+//
+bool ChattyLogBuffer::Prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
+ LogReaderThread* oldest = nullptr;
+ bool busy = false;
+ bool clearAll = pruneRows == ULONG_MAX;
+
+ auto reader_threads_lock = std::lock_guard{reader_list()->reader_threads_lock()};
+
+ // Region locked?
+ for (const auto& reader_thread : reader_list()->reader_threads()) {
+ if (!reader_thread->IsWatching(id)) {
+ continue;
+ }
+ if (!oldest || oldest->start() > reader_thread->start() ||
+ (oldest->start() == reader_thread->start() &&
+ reader_thread->deadline().time_since_epoch().count() != 0)) {
+ oldest = reader_thread.get();
+ }
+ }
+
+ LogBufferElementCollection::iterator it;
+
+ if (__predict_false(caller_uid != AID_ROOT)) { // unlikely
+ // Only here if clear all request from non system source, so chatty
+ // filter logistics is not required.
+ it = GetOldest(id);
+ while (it != logs().end()) {
+ LogBufferElement& element = *it;
+
+ if (element.log_id() != id || element.uid() != caller_uid) {
+ ++it;
+ continue;
+ }
+
+ if (oldest && oldest->start() <= element.sequence()) {
+ busy = true;
+ KickReader(oldest, id, pruneRows);
+ break;
+ }
+
+ it = Erase(it);
+ if (--pruneRows == 0) {
+ break;
+ }
+ }
+ return busy;
+ }
+
+ // prune by worst offenders; by blacklist, UID, and by PID of system UID
+ bool hasBlacklist = (id != LOG_ID_SECURITY) && prune_->naughty();
+ while (!clearAll && (pruneRows > 0)) {
+ // recalculate the worst offender on every batched pass
+ int worst = -1; // not valid for uid() or getKey()
+ size_t worst_sizes = 0;
+ size_t second_worst_sizes = 0;
+ pid_t worstPid = 0; // POSIX guarantees PID != 0
+
+ if (worstUidEnabledForLogid(id) && prune_->worstUidEnabled()) {
+ // Calculate threshold as 12.5% of available storage
+ size_t threshold = max_size(id) / 8;
+
+ if (id == LOG_ID_EVENTS || id == LOG_ID_SECURITY) {
+ stats()->WorstTwoTags(threshold, &worst, &worst_sizes, &second_worst_sizes);
+ // per-pid filter for AID_SYSTEM sources is too complex
+ } else {
+ stats()->WorstTwoUids(id, threshold, &worst, &worst_sizes, &second_worst_sizes);
+
+ if (worst == AID_SYSTEM && prune_->worstPidOfSystemEnabled()) {
+ stats()->WorstTwoSystemPids(id, worst_sizes, &worstPid, &second_worst_sizes);
+ }
+ }
+ }
+
+ // skip if we have neither worst nor naughty filters
+ if ((worst == -1) && !hasBlacklist) {
+ break;
+ }
+
+ bool kick = false;
+ bool leading = true; // true if starting from the oldest log entry, false if starting from
+ // a specific chatty entry.
+ // Perform at least one mandatory garbage collection cycle in following
+ // - clear leading chatty tags
+ // - coalesce chatty tags
+ // - check age-out of preserved logs
+ bool gc = pruneRows <= 1;
+ if (!gc && (worst != -1)) {
+ { // begin scope for worst found iterator
+ LogBufferIteratorMap::iterator found = mLastWorst[id].find(worst);
+ if (found != mLastWorst[id].end() && found->second != logs().end()) {
+ leading = false;
+ it = found->second;
+ }
+ }
+ if (worstPid) { // begin scope for pid worst found iterator
+ // FYI: worstPid only set if !LOG_ID_EVENTS and
+ // !LOG_ID_SECURITY, not going to make that assumption ...
+ LogBufferPidIteratorMap::iterator found = mLastWorstPidOfSystem[id].find(worstPid);
+ if (found != mLastWorstPidOfSystem[id].end() && found->second != logs().end()) {
+ leading = false;
+ it = found->second;
+ }
+ }
+ }
+ if (leading) {
+ it = GetOldest(id);
+ }
+ static const log_time too_old{EXPIRE_HOUR_THRESHOLD * 60 * 60, 0};
+ LogBufferElementCollection::iterator lastt;
+ lastt = logs().end();
+ --lastt;
+ LogBufferElementLast last;
+ while (it != logs().end()) {
+ LogBufferElement& element = *it;
+
+ if (oldest && oldest->start() <= element.sequence()) {
+ busy = true;
+ // Do not let chatty eliding trigger any reader mitigation
+ break;
+ }
+
+ if (element.log_id() != id) {
+ ++it;
+ continue;
+ }
+ // below this point element->log_id() == id
+
+ uint16_t dropped = element.dropped_count();
+
+ // remove any leading drops
+ if (leading && dropped) {
+ it = Erase(it);
+ continue;
+ }
+
+ if (dropped && last.coalesce(&element, dropped)) {
+ it = Erase(it, true);
+ continue;
+ }
+
+ int key = (id == LOG_ID_EVENTS || id == LOG_ID_SECURITY) ? element.GetTag()
+ : element.uid();
+
+ if (hasBlacklist && prune_->naughty(&element)) {
+ last.clear(&element);
+ it = Erase(it);
+ if (dropped) {
+ continue;
+ }
+
+ pruneRows--;
+ if (pruneRows == 0) {
+ break;
+ }
+
+ if (key == worst) {
+ kick = true;
+ if (worst_sizes < second_worst_sizes) {
+ break;
+ }
+ worst_sizes -= element.msg_len();
+ }
+ continue;
+ }
+
+ if (element.realtime() < (lastt->realtime() - too_old) ||
+ element.realtime() > lastt->realtime()) {
+ break;
+ }
+
+ if (dropped) {
+ last.add(&element);
+ if (worstPid && ((!gc && element.pid() == worstPid) ||
+ mLastWorstPidOfSystem[id].find(element.pid()) ==
+ mLastWorstPidOfSystem[id].end())) {
+ // element->uid() may not be AID_SYSTEM, next best
+ // watermark if current one empty. id is not LOG_ID_EVENTS
+ // or LOG_ID_SECURITY because of worstPid check.
+ mLastWorstPidOfSystem[id][element.pid()] = it;
+ }
+ if ((!gc && !worstPid && (key == worst)) ||
+ (mLastWorst[id].find(key) == mLastWorst[id].end())) {
+ mLastWorst[id][key] = it;
+ }
+ ++it;
+ continue;
+ }
+
+ if (key != worst || (worstPid && element.pid() != worstPid)) {
+ leading = false;
+ last.clear(&element);
+ ++it;
+ continue;
+ }
+ // key == worst below here
+ // If worstPid set, then element->pid() == worstPid below here
+
+ pruneRows--;
+ if (pruneRows == 0) {
+ break;
+ }
+
+ kick = true;
+
+ uint16_t len = element.msg_len();
+
+ // do not create any leading drops
+ if (leading) {
+ it = Erase(it);
+ } else {
+ stats()->Drop(element.ToLogStatisticsElement());
+ element.SetDropped(1);
+ if (last.coalesce(&element, 1)) {
+ it = Erase(it, true);
+ } else {
+ last.add(&element);
+ if (worstPid && (!gc || mLastWorstPidOfSystem[id].find(worstPid) ==
+ mLastWorstPidOfSystem[id].end())) {
+ // element->uid() may not be AID_SYSTEM, next best
+ // watermark if current one empty. id is not
+ // LOG_ID_EVENTS or LOG_ID_SECURITY because of worstPid.
+ mLastWorstPidOfSystem[id][worstPid] = it;
+ }
+ if ((!gc && !worstPid) || mLastWorst[id].find(worst) == mLastWorst[id].end()) {
+ mLastWorst[id][worst] = it;
+ }
+ ++it;
+ }
+ }
+ if (worst_sizes < second_worst_sizes) {
+ break;
+ }
+ worst_sizes -= len;
+ }
+ last.clear();
+
+ if (!kick || !prune_->worstUidEnabled()) {
+ break; // the following loop will ask bad clients to skip/drop
+ }
+ }
+
+ bool whitelist = false;
+ bool hasWhitelist = (id != LOG_ID_SECURITY) && prune_->nice() && !clearAll;
+ it = GetOldest(id);
+ while ((pruneRows > 0) && (it != logs().end())) {
+ LogBufferElement& element = *it;
+
+ if (element.log_id() != id) {
+ it++;
+ continue;
+ }
+
+ if (oldest && oldest->start() <= element.sequence()) {
+ busy = true;
+ if (!whitelist) KickReader(oldest, id, pruneRows);
+ break;
+ }
+
+ if (hasWhitelist && !element.dropped_count() && prune_->nice(&element)) {
+ // WhiteListed
+ whitelist = true;
+ it++;
+ continue;
+ }
+
+ it = Erase(it);
+ pruneRows--;
+ }
+
+ // Do not save the whitelist if we are reader range limited
+ if (whitelist && (pruneRows > 0)) {
+ it = GetOldest(id);
+ while ((it != logs().end()) && (pruneRows > 0)) {
+ LogBufferElement& element = *it;
+
+ if (element.log_id() != id) {
+ ++it;
+ continue;
+ }
+
+ if (oldest && oldest->start() <= element.sequence()) {
+ busy = true;
+ KickReader(oldest, id, pruneRows);
+ break;
+ }
+
+ it = Erase(it);
+ pruneRows--;
+ }
+ }
+
+ return (pruneRows > 0) && busy;
+}
diff --git a/logd/ChattyLogBuffer.h b/logd/ChattyLogBuffer.h
new file mode 100644
index 0000000..6f60272
--- /dev/null
+++ b/logd/ChattyLogBuffer.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2012-2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <sys/types.h>
+
+#include <list>
+#include <optional>
+#include <string>
+
+#include <android-base/thread_annotations.h>
+#include <android/log.h>
+#include <private/android_filesystem_config.h>
+#include <sysutils/SocketClient.h>
+
+#include "LogBuffer.h"
+#include "LogBufferElement.h"
+#include "LogReaderList.h"
+#include "LogReaderThread.h"
+#include "LogStatistics.h"
+#include "LogTags.h"
+#include "LogWhiteBlackList.h"
+#include "LogWriter.h"
+#include "SimpleLogBuffer.h"
+#include "rwlock.h"
+
+typedef std::list<LogBufferElement> LogBufferElementCollection;
+
+class ChattyLogBuffer : public SimpleLogBuffer {
+ // watermark of any worst/chatty uid processing
+ typedef std::unordered_map<uid_t, LogBufferElementCollection::iterator> LogBufferIteratorMap;
+ LogBufferIteratorMap mLastWorst[LOG_ID_MAX] GUARDED_BY(lock_);
+ // watermark of any worst/chatty pid of system processing
+ typedef std::unordered_map<pid_t, LogBufferElementCollection::iterator> LogBufferPidIteratorMap;
+ LogBufferPidIteratorMap mLastWorstPidOfSystem[LOG_ID_MAX] GUARDED_BY(lock_);
+
+ public:
+ ChattyLogBuffer(LogReaderList* reader_list, LogTags* tags, PruneList* prune,
+ LogStatistics* stats);
+ ~ChattyLogBuffer();
+
+ protected:
+ bool Prune(log_id_t id, unsigned long pruneRows, uid_t uid) REQUIRES(lock_) override;
+ void LogInternal(LogBufferElement&& elem) REQUIRES(lock_) override;
+
+ private:
+ LogBufferElementCollection::iterator Erase(LogBufferElementCollection::iterator it,
+ bool coalesce = false) REQUIRES(lock_);
+
+ PruneList* prune_;
+
+ // This always contains a copy of the last message logged, for deduplication.
+ std::optional<LogBufferElement> last_logged_elements_[LOG_ID_MAX] GUARDED_BY(lock_);
+ // This contains an element if duplicate messages are seen.
+ // Its `dropped` count is `duplicates seen - 1`.
+ std::optional<LogBufferElement> duplicate_elements_[LOG_ID_MAX] GUARDED_BY(lock_);
+};
diff --git a/logd/ChattyLogBufferTest.cpp b/logd/ChattyLogBufferTest.cpp
new file mode 100644
index 0000000..3d9005a
--- /dev/null
+++ b/logd/ChattyLogBufferTest.cpp
@@ -0,0 +1,335 @@
+/*
+ * 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 "LogBufferTest.h"
+
+class ChattyLogBufferTest : public LogBufferTest {};
+
+TEST_P(ChattyLogBufferTest, deduplication_simple) {
+ auto make_message = [&](uint32_t sec, const char* tag, const char* msg,
+ bool regex = false) -> LogMessage {
+ logger_entry entry = {
+ .pid = 1, .tid = 1, .sec = sec, .nsec = 1, .lid = LOG_ID_MAIN, .uid = 0};
+ std::string message;
+ message.push_back(ANDROID_LOG_INFO);
+ message.append(tag);
+ message.push_back('\0');
+ message.append(msg);
+ message.push_back('\0');
+ return {entry, message, regex};
+ };
+
+ // clang-format off
+ std::vector<LogMessage> log_messages = {
+ make_message(0, "test_tag", "duplicate"),
+ make_message(1, "test_tag", "duplicate"),
+ make_message(2, "test_tag", "not_same"),
+ make_message(3, "test_tag", "duplicate"),
+ make_message(4, "test_tag", "duplicate"),
+ make_message(5, "test_tag", "not_same"),
+ make_message(6, "test_tag", "duplicate"),
+ make_message(7, "test_tag", "duplicate"),
+ make_message(8, "test_tag", "duplicate"),
+ make_message(9, "test_tag", "not_same"),
+ make_message(10, "test_tag", "duplicate"),
+ make_message(11, "test_tag", "duplicate"),
+ make_message(12, "test_tag", "duplicate"),
+ make_message(13, "test_tag", "duplicate"),
+ make_message(14, "test_tag", "duplicate"),
+ make_message(15, "test_tag", "duplicate"),
+ make_message(16, "test_tag", "not_same"),
+ make_message(100, "test_tag", "duplicate"),
+ make_message(200, "test_tag", "duplicate"),
+ make_message(300, "test_tag", "duplicate"),
+ };
+ // clang-format on
+ FixupMessages(&log_messages);
+ LogMessages(log_messages);
+
+ std::vector<LogMessage> read_log_messages;
+ std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, nullptr));
+ std::unique_ptr<FlushToState> flush_to_state = log_buffer_->CreateFlushToState(1, kLogMaskAll);
+ EXPECT_TRUE(log_buffer_->FlushTo(test_writer.get(), *flush_to_state, nullptr));
+
+ std::vector<LogMessage> expected_log_messages = {
+ make_message(0, "test_tag", "duplicate"),
+ make_message(1, "test_tag", "duplicate"),
+ make_message(2, "test_tag", "not_same"),
+ make_message(3, "test_tag", "duplicate"),
+ make_message(4, "test_tag", "duplicate"),
+ make_message(5, "test_tag", "not_same"),
+ // 3 duplicate logs together print the first, a 1 count chatty message, then the last.
+ make_message(6, "test_tag", "duplicate"),
+ make_message(7, "chatty", "uid=0\\([^\\)]+\\) [^ ]+ identical 1 line", true),
+ make_message(8, "test_tag", "duplicate"),
+ make_message(9, "test_tag", "not_same"),
+ // 6 duplicate logs together print the first, a 4 count chatty message, then the last.
+ make_message(10, "test_tag", "duplicate"),
+ make_message(14, "chatty", "uid=0\\([^\\)]+\\) [^ ]+ identical 4 lines", true),
+ make_message(15, "test_tag", "duplicate"),
+ make_message(16, "test_tag", "not_same"),
+ // duplicate logs > 1 minute apart are not deduplicated.
+ make_message(100, "test_tag", "duplicate"),
+ make_message(200, "test_tag", "duplicate"),
+ make_message(300, "test_tag", "duplicate"),
+ };
+ FixupMessages(&expected_log_messages);
+ CompareLogMessages(expected_log_messages, read_log_messages);
+};
+
+TEST_P(ChattyLogBufferTest, deduplication_overflow) {
+ auto make_message = [&](uint32_t sec, const char* tag, const char* msg,
+ bool regex = false) -> LogMessage {
+ logger_entry entry = {
+ .pid = 1, .tid = 1, .sec = sec, .nsec = 1, .lid = LOG_ID_MAIN, .uid = 0};
+ std::string message;
+ message.push_back(ANDROID_LOG_INFO);
+ message.append(tag);
+ message.push_back('\0');
+ message.append(msg);
+ message.push_back('\0');
+ return {entry, message, regex};
+ };
+
+ uint32_t sec = 0;
+ std::vector<LogMessage> log_messages = {
+ make_message(sec++, "test_tag", "normal"),
+ };
+ size_t expired_per_chatty_message = std::numeric_limits<uint16_t>::max();
+ for (size_t i = 0; i < expired_per_chatty_message + 3; ++i) {
+ log_messages.emplace_back(make_message(sec++, "test_tag", "duplicate"));
+ }
+ log_messages.emplace_back(make_message(sec++, "test_tag", "normal"));
+ FixupMessages(&log_messages);
+ LogMessages(log_messages);
+
+ std::vector<LogMessage> read_log_messages;
+ std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, nullptr));
+ std::unique_ptr<FlushToState> flush_to_state = log_buffer_->CreateFlushToState(1, kLogMaskAll);
+ EXPECT_TRUE(log_buffer_->FlushTo(test_writer.get(), *flush_to_state, nullptr));
+
+ std::vector<LogMessage> expected_log_messages = {
+ make_message(0, "test_tag", "normal"),
+ make_message(1, "test_tag", "duplicate"),
+ make_message(expired_per_chatty_message + 1, "chatty",
+ "uid=0\\([^\\)]+\\) [^ ]+ identical 65535 lines", true),
+ make_message(expired_per_chatty_message + 2, "chatty",
+ "uid=0\\([^\\)]+\\) [^ ]+ identical 1 line", true),
+ make_message(expired_per_chatty_message + 3, "test_tag", "duplicate"),
+ make_message(expired_per_chatty_message + 4, "test_tag", "normal"),
+ };
+ FixupMessages(&expected_log_messages);
+ CompareLogMessages(expected_log_messages, read_log_messages);
+}
+
+TEST_P(ChattyLogBufferTest, deduplication_liblog) {
+ auto make_message = [&](uint32_t sec, int32_t tag, int32_t count) -> LogMessage {
+ logger_entry entry = {
+ .pid = 1, .tid = 1, .sec = sec, .nsec = 1, .lid = LOG_ID_EVENTS, .uid = 0};
+ android_log_event_int_t liblog_event = {
+ .header.tag = tag, .payload.type = EVENT_TYPE_INT, .payload.data = count};
+ return {entry, std::string(reinterpret_cast<char*>(&liblog_event), sizeof(liblog_event)),
+ false};
+ };
+
+ // LIBLOG_LOG_TAG
+ std::vector<LogMessage> log_messages = {
+ make_message(0, 1234, 1),
+ make_message(1, LIBLOG_LOG_TAG, 3),
+ make_message(2, 1234, 2),
+ make_message(3, LIBLOG_LOG_TAG, 3),
+ make_message(4, LIBLOG_LOG_TAG, 4),
+ make_message(5, 1234, 223),
+ make_message(6, LIBLOG_LOG_TAG, 2),
+ make_message(7, LIBLOG_LOG_TAG, 3),
+ make_message(8, LIBLOG_LOG_TAG, 4),
+ make_message(9, 1234, 227),
+ make_message(10, LIBLOG_LOG_TAG, 1),
+ make_message(11, LIBLOG_LOG_TAG, 3),
+ make_message(12, LIBLOG_LOG_TAG, 2),
+ make_message(13, LIBLOG_LOG_TAG, 3),
+ make_message(14, LIBLOG_LOG_TAG, 5),
+ make_message(15, 1234, 227),
+ make_message(16, LIBLOG_LOG_TAG, 2),
+ make_message(17, LIBLOG_LOG_TAG, std::numeric_limits<int32_t>::max()),
+ make_message(18, LIBLOG_LOG_TAG, 3),
+ make_message(19, LIBLOG_LOG_TAG, 5),
+ make_message(20, 1234, 227),
+ };
+ FixupMessages(&log_messages);
+ LogMessages(log_messages);
+
+ std::vector<LogMessage> read_log_messages;
+ std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, nullptr));
+ std::unique_ptr<FlushToState> flush_to_state = log_buffer_->CreateFlushToState(1, kLogMaskAll);
+ EXPECT_TRUE(log_buffer_->FlushTo(test_writer.get(), *flush_to_state, nullptr));
+
+ std::vector<LogMessage> expected_log_messages = {
+ make_message(0, 1234, 1),
+ make_message(1, LIBLOG_LOG_TAG, 3),
+ make_message(2, 1234, 2),
+ make_message(3, LIBLOG_LOG_TAG, 3),
+ make_message(4, LIBLOG_LOG_TAG, 4),
+ make_message(5, 1234, 223),
+ // More than 2 liblog events (3 here), sum their value into the third message.
+ make_message(6, LIBLOG_LOG_TAG, 2),
+ make_message(8, LIBLOG_LOG_TAG, 7),
+ make_message(9, 1234, 227),
+ // More than 2 liblog events (5 here), sum their value into the third message.
+ make_message(10, LIBLOG_LOG_TAG, 1),
+ make_message(14, LIBLOG_LOG_TAG, 13),
+ make_message(15, 1234, 227),
+ // int32_t max is the max for a chatty message, beyond that we must use new messages.
+ make_message(16, LIBLOG_LOG_TAG, 2),
+ make_message(17, LIBLOG_LOG_TAG, std::numeric_limits<int32_t>::max()),
+ make_message(19, LIBLOG_LOG_TAG, 8),
+ make_message(20, 1234, 227),
+ };
+ FixupMessages(&expected_log_messages);
+ CompareLogMessages(expected_log_messages, read_log_messages);
+};
+
+TEST_P(ChattyLogBufferTest, no_leading_chatty_simple) {
+ auto make_message = [&](uint32_t sec, int32_t pid, uint32_t uid, uint32_t lid, const char* tag,
+ const char* msg, bool regex = false) -> LogMessage {
+ logger_entry entry = {.pid = pid, .tid = 1, .sec = sec, .nsec = 1, .lid = lid, .uid = uid};
+ std::string message;
+ message.push_back(ANDROID_LOG_INFO);
+ message.append(tag);
+ message.push_back('\0');
+ message.append(msg);
+ message.push_back('\0');
+ return {entry, message, regex};
+ };
+
+ // clang-format off
+ std::vector<LogMessage> log_messages = {
+ make_message(1, 1, 1, LOG_ID_MAIN, "test_tag", "duplicate1"),
+ make_message(2, 2, 2, LOG_ID_SYSTEM, "test_tag", "duplicate2"),
+ make_message(3, 2, 2, LOG_ID_SYSTEM, "test_tag", "duplicate2"),
+ make_message(4, 2, 2, LOG_ID_SYSTEM, "test_tag", "duplicate2"),
+ make_message(6, 2, 2, LOG_ID_SYSTEM, "test_tag", "not duplicate2"),
+ make_message(7, 1, 1, LOG_ID_MAIN, "test_tag", "duplicate1"),
+ make_message(8, 1, 1, LOG_ID_MAIN, "test_tag", "duplicate1"),
+ make_message(9, 1, 1, LOG_ID_MAIN, "test_tag", "duplicate1"),
+ make_message(10, 1, 1, LOG_ID_MAIN, "test_tag", "not duplicate1"),
+ };
+ // clang-format on
+ FixupMessages(&log_messages);
+ LogMessages(log_messages);
+
+ // After logging log_messages, the below is what should be in the buffer:
+ // PID=1, LOG_ID_MAIN duplicate1
+ // [1] PID=2, LOG_ID_SYSTEM duplicate2
+ // PID=2, LOG_ID_SYSTEM chatty drop
+ // PID=2, LOG_ID_SYSTEM duplicate2
+ // PID=2, LOG_ID_SYSTEM not duplicate2
+ // [2] PID=1, LOG_ID_MAIN chatty drop
+ // [3] PID=1, LOG_ID_MAIN duplicate1
+ // PID=1, LOG_ID_MAIN not duplicate1
+
+ // We then read from the 2nd sequence number, starting from log message [1], but filtering out
+ // everything but PID=1, which results in us starting with log message [2], which is a chatty
+ // drop. Code prior to this test case would erroneously print it. The intended behavior that
+ // this test checks prints logs starting from log message [3].
+
+ // clang-format off
+ std::vector<LogMessage> expected_log_messages = {
+ make_message(9, 1, 1, LOG_ID_MAIN, "test_tag", "duplicate1"),
+ make_message(10, 1, 1, LOG_ID_MAIN, "test_tag", "not duplicate1"),
+ };
+ FixupMessages(&expected_log_messages);
+ // clang-format on
+
+ std::vector<LogMessage> read_log_messages;
+ bool released = false;
+ {
+ auto lock = std::unique_lock{reader_list_.reader_threads_lock()};
+ std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, &released));
+ std::unique_ptr<LogReaderThread> log_reader(
+ new LogReaderThread(log_buffer_.get(), &reader_list_, std::move(test_writer), true,
+ 0, ~0, 1, {}, 2, {}));
+ reader_list_.reader_threads().emplace_back(std::move(log_reader));
+ }
+
+ while (!released) {
+ usleep(5000);
+ }
+
+ CompareLogMessages(expected_log_messages, read_log_messages);
+}
+
+TEST_P(ChattyLogBufferTest, no_leading_chatty_tail) {
+ auto make_message = [&](uint32_t sec, const char* tag, const char* msg,
+ bool regex = false) -> LogMessage {
+ logger_entry entry = {
+ .pid = 1, .tid = 1, .sec = sec, .nsec = 1, .lid = LOG_ID_MAIN, .uid = 0};
+ std::string message;
+ message.push_back(ANDROID_LOG_INFO);
+ message.append(tag);
+ message.push_back('\0');
+ message.append(msg);
+ message.push_back('\0');
+ return {entry, message, regex};
+ };
+
+ // clang-format off
+ std::vector<LogMessage> log_messages = {
+ make_message(1, "test_tag", "duplicate"),
+ make_message(2, "test_tag", "duplicate"),
+ make_message(3, "test_tag", "duplicate"),
+ make_message(4, "test_tag", "not_duplicate"),
+ };
+ // clang-format on
+ FixupMessages(&log_messages);
+ LogMessages(log_messages);
+
+ // After logging log_messages, the below is what should be in the buffer:
+ // "duplicate"
+ // chatty
+ // "duplicate"
+ // "not duplicate"
+
+ // We then read the tail 3 messages expecting there to not be a chatty message, meaning that we
+ // should only see the last two messages.
+
+ // clang-format off
+ std::vector<LogMessage> expected_log_messages = {
+ make_message(3, "test_tag", "duplicate"),
+ make_message(4, "test_tag", "not_duplicate"),
+ };
+ FixupMessages(&expected_log_messages);
+ // clang-format on
+
+ std::vector<LogMessage> read_log_messages;
+ bool released = false;
+ {
+ auto lock = std::unique_lock{reader_list_.reader_threads_lock()};
+ std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, &released));
+ std::unique_ptr<LogReaderThread> log_reader(
+ new LogReaderThread(log_buffer_.get(), &reader_list_, std::move(test_writer), true,
+ 3, ~0, 0, {}, 1, {}));
+ reader_list_.reader_threads().emplace_back(std::move(log_reader));
+ }
+
+ while (!released) {
+ usleep(5000);
+ }
+
+ CompareLogMessages(expected_log_messages, read_log_messages);
+}
+
+INSTANTIATE_TEST_CASE_P(ChattyLogBufferTests, ChattyLogBufferTest, testing::Values("chatty"));
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
index 694b5fa..9764c44 100644
--- a/logd/CommandListener.cpp
+++ b/logd/CommandListener.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "CommandListener.h"
+
#include <arpa/inet.h>
#include <ctype.h>
#include <dirent.h>
@@ -29,46 +31,30 @@
#include <string>
+#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <cutils/sockets.h>
+#include <log/log_properties.h>
#include <private/android_filesystem_config.h>
#include <sysutils/SocketClient.h>
-#include "CommandListener.h"
-#include "LogCommand.h"
-#include "LogUtils.h"
+#include "LogPermissions.h"
-CommandListener::CommandListener(LogBuffer* buf, LogReader* /*reader*/,
- LogListener* /*swl*/)
- : FrameworkListener(getLogSocket()) {
- // registerCmd(new ShutdownCmd(buf, writer, swl));
- registerCmd(new ClearCmd(buf));
- registerCmd(new GetBufSizeCmd(buf));
- registerCmd(new SetBufSizeCmd(buf));
- registerCmd(new GetBufSizeUsedCmd(buf));
- registerCmd(new GetStatisticsCmd(buf));
- registerCmd(new SetPruneListCmd(buf));
- registerCmd(new GetPruneListCmd(buf));
- registerCmd(new GetEventTagCmd(buf));
- registerCmd(new ReinitCmd());
+CommandListener::CommandListener(LogBuffer* buf, LogTags* tags, PruneList* prune,
+ LogStatistics* stats)
+ : FrameworkListener(getLogSocket()), buf_(buf), tags_(tags), prune_(prune), stats_(stats) {
+ registerCmd(new ClearCmd(this));
+ registerCmd(new GetBufSizeCmd(this));
+ registerCmd(new SetBufSizeCmd(this));
+ registerCmd(new GetBufSizeUsedCmd(this));
+ registerCmd(new GetStatisticsCmd(this));
+ registerCmd(new SetPruneListCmd(this));
+ registerCmd(new GetPruneListCmd(this));
+ registerCmd(new GetEventTagCmd(this));
+ registerCmd(new ReinitCmd(this));
registerCmd(new ExitCmd(this));
}
-CommandListener::ShutdownCmd::ShutdownCmd(LogReader* reader, LogListener* swl)
- : LogCommand("shutdown"), mReader(*reader), mSwl(*swl) {
-}
-
-int CommandListener::ShutdownCmd::runCommand(SocketClient* /*cli*/,
- int /*argc*/, char** /*argv*/) {
- mSwl.stopListener();
- mReader.stopListener();
- exit(0);
-}
-
-CommandListener::ClearCmd::ClearCmd(LogBuffer* buf)
- : LogCommand("clear"), mBuf(*buf) {
-}
-
static void setname() {
static bool name_set;
if (!name_set) {
@@ -96,14 +82,10 @@
return 0;
}
- cli->sendMsg(mBuf.clear((log_id_t)id, uid) ? "busy" : "success");
+ cli->sendMsg(buf()->Clear((log_id_t)id, uid) ? "busy" : "success");
return 0;
}
-CommandListener::GetBufSizeCmd::GetBufSizeCmd(LogBuffer* buf)
- : LogCommand("getLogSize"), mBuf(*buf) {
-}
-
int CommandListener::GetBufSizeCmd::runCommand(SocketClient* cli, int argc,
char** argv) {
setname();
@@ -118,17 +100,13 @@
return 0;
}
- unsigned long size = mBuf.getSize((log_id_t)id);
+ unsigned long size = buf()->GetSize((log_id_t)id);
char buf[512];
snprintf(buf, sizeof(buf), "%lu", size);
cli->sendMsg(buf);
return 0;
}
-CommandListener::SetBufSizeCmd::SetBufSizeCmd(LogBuffer* buf)
- : LogCommand("setLogSize"), mBuf(*buf) {
-}
-
int CommandListener::SetBufSizeCmd::runCommand(SocketClient* cli, int argc,
char** argv) {
setname();
@@ -149,7 +127,7 @@
}
unsigned long size = atol(argv[2]);
- if (mBuf.setSize((log_id_t)id, size)) {
+ if (buf()->SetSize((log_id_t)id, size)) {
cli->sendMsg("Range Error");
return 0;
}
@@ -158,10 +136,6 @@
return 0;
}
-CommandListener::GetBufSizeUsedCmd::GetBufSizeUsedCmd(LogBuffer* buf)
- : LogCommand("getLogSizeUsed"), mBuf(*buf) {
-}
-
int CommandListener::GetBufSizeUsedCmd::runCommand(SocketClient* cli, int argc,
char** argv) {
setname();
@@ -176,17 +150,13 @@
return 0;
}
- unsigned long size = mBuf.getSizeUsed((log_id_t)id);
+ unsigned long size = stats()->Sizes((log_id_t)id);
char buf[512];
snprintf(buf, sizeof(buf), "%lu", size);
cli->sendMsg(buf);
return 0;
}
-CommandListener::GetStatisticsCmd::GetStatisticsCmd(LogBuffer* buf)
- : LogCommand("getStatistics"), mBuf(*buf) {
-}
-
// This returns a string with a length prefix with the format <length>\n<data>\n\f. The length
// prefix includes the length of the prefix itself.
static std::string PackageString(const std::string& str) {
@@ -241,25 +211,17 @@
}
}
- cli->sendMsg(PackageString(mBuf.formatStatistics(uid, pid, logMask)).c_str());
+ cli->sendMsg(PackageString(stats()->Format(uid, pid, logMask)).c_str());
return 0;
}
-CommandListener::GetPruneListCmd::GetPruneListCmd(LogBuffer* buf)
- : LogCommand("getPruneList"), mBuf(*buf) {
-}
-
int CommandListener::GetPruneListCmd::runCommand(SocketClient* cli,
int /*argc*/, char** /*argv*/) {
setname();
- cli->sendMsg(PackageString(mBuf.formatPrune()).c_str());
+ cli->sendMsg(PackageString(prune()->format()).c_str());
return 0;
}
-CommandListener::SetPruneListCmd::SetPruneListCmd(LogBuffer* buf)
- : LogCommand("setPruneList"), mBuf(*buf) {
-}
-
int CommandListener::SetPruneListCmd::runCommand(SocketClient* cli, int argc,
char** argv) {
setname();
@@ -276,7 +238,7 @@
str += argv[i];
}
- int ret = mBuf.initPrune(str.c_str());
+ int ret = prune()->init(str.c_str());
if (ret) {
cli->sendMsg("Invalid");
@@ -288,10 +250,6 @@
return 0;
}
-CommandListener::GetEventTagCmd::GetEventTagCmd(LogBuffer* buf)
- : LogCommand("getEventTag"), mBuf(*buf) {
-}
-
int CommandListener::GetEventTagCmd::runCommand(SocketClient* cli, int argc,
char** argv) {
setname();
@@ -328,39 +286,45 @@
cli->sendMsg("can not mix id= with either format= or name=");
return 0;
}
- cli->sendMsg(PackageString(mBuf.formatEntry(atoi(id), uid)).c_str());
+ cli->sendMsg(PackageString(tags()->formatEntry(atoi(id), uid)).c_str());
return 0;
}
- cli->sendMsg(PackageString(mBuf.formatGetEventTag(uid, name, format)).c_str());
+ cli->sendMsg(PackageString(tags()->formatGetEventTag(uid, name, format)).c_str());
return 0;
}
-CommandListener::ReinitCmd::ReinitCmd() : LogCommand("reinit") {
-}
-
int CommandListener::ReinitCmd::runCommand(SocketClient* cli, int /*argc*/,
char** /*argv*/) {
setname();
- reinit_signal_handler(SIGHUP);
+ LOG(INFO) << "logd reinit";
+ buf()->Init();
+ prune()->init(nullptr);
+
+ // This only works on userdebug and eng devices to re-read the
+ // /data/misc/logd/event-log-tags file right after /data is mounted.
+ // The operation is near to boot and should only happen once. There
+ // are races associated with its use since it can trigger a Rebuild
+ // of the file, but that is a can-not-happen since the file was not
+ // read yet. More dangerous if called later, but if all is well it
+ // should just skip over everything and not write any new entries.
+ if (__android_log_is_debuggable()) {
+ tags()->ReadFileEventLogTags(tags()->debug_event_log_tags);
+ }
cli->sendMsg("success");
return 0;
}
-CommandListener::ExitCmd::ExitCmd(CommandListener* parent)
- : LogCommand("EXIT"), mParent(*parent) {
-}
-
int CommandListener::ExitCmd::runCommand(SocketClient* cli, int /*argc*/,
char** /*argv*/) {
setname();
cli->sendMsg("success");
- release(cli);
+ parent_->release(cli);
return 0;
}
diff --git a/logd/CommandListener.h b/logd/CommandListener.h
index ed99419..a55a393 100644
--- a/logd/CommandListener.h
+++ b/logd/CommandListener.h
@@ -14,85 +14,55 @@
* limitations under the License.
*/
-#ifndef _COMMANDLISTENER_H__
-#define _COMMANDLISTENER_H__
+#pragma once
+#include <sysutils/FrameworkCommand.h>
#include <sysutils/FrameworkListener.h>
-#include "LogBuffer.h"
-#include "LogCommand.h"
-#include "LogListener.h"
-#include "LogReader.h"
-// See main.cpp for implementation
-void reinit_signal_handler(int /*signal*/);
+#include "LogBuffer.h"
+#include "LogListener.h"
+#include "LogStatistics.h"
+#include "LogTags.h"
+#include "LogWhiteBlackList.h"
class CommandListener : public FrameworkListener {
- public:
- CommandListener(LogBuffer* buf, LogReader* reader, LogListener* swl);
- virtual ~CommandListener() {
- }
+ public:
+ CommandListener(LogBuffer* buf, LogTags* tags, PruneList* prune, LogStatistics* log_statistics);
+ virtual ~CommandListener() {}
- private:
+ private:
static int getLogSocket();
- class ShutdownCmd : public LogCommand {
- LogReader& mReader;
- LogListener& mSwl;
+ LogBuffer* buf_;
+ LogTags* tags_;
+ PruneList* prune_;
+ LogStatistics* stats_;
- public:
- ShutdownCmd(LogReader* reader, LogListener* swl);
- virtual ~ShutdownCmd() {
- }
- int runCommand(SocketClient* c, int argc, char** argv);
- };
-
-#define LogBufferCmd(name) \
- class name##Cmd : public LogCommand { \
- LogBuffer& mBuf; \
- \
- public: \
- explicit name##Cmd(LogBuffer* buf); \
- virtual ~name##Cmd() { \
- } \
- int runCommand(SocketClient* c, int argc, char** argv); \
+#define LogCmd(name, command_string) \
+ class name##Cmd : public FrameworkCommand { \
+ public: \
+ explicit name##Cmd(CommandListener* parent) \
+ : FrameworkCommand(#command_string), parent_(parent) {} \
+ virtual ~name##Cmd() {} \
+ int runCommand(SocketClient* c, int argc, char** argv); \
+ \
+ private: \
+ LogBuffer* buf() const { return parent_->buf_; } \
+ LogTags* tags() const { return parent_->tags_; } \
+ PruneList* prune() const { return parent_->prune_; } \
+ LogStatistics* stats() const { return parent_->stats_; } \
+ CommandListener* parent_; \
}
- LogBufferCmd(Clear);
- LogBufferCmd(GetBufSize);
- LogBufferCmd(SetBufSize);
- LogBufferCmd(GetBufSizeUsed);
- LogBufferCmd(GetStatistics);
- LogBufferCmd(GetPruneList);
- LogBufferCmd(SetPruneList);
- LogBufferCmd(GetEventTag);
-
-#define LogCmd(name) \
- class name##Cmd : public LogCommand { \
- public: \
- name##Cmd(); \
- virtual ~name##Cmd() { \
- } \
- int runCommand(SocketClient* c, int argc, char** argv); \
- }
-
- LogCmd(Reinit);
-
-#define LogParentCmd(name) \
- class name##Cmd : public LogCommand { \
- CommandListener& mParent; \
- \
- public: \
- name##Cmd(); \
- explicit name##Cmd(CommandListener* parent); \
- virtual ~name##Cmd() { \
- } \
- int runCommand(SocketClient* c, int argc, char** argv); \
- void release(SocketClient* c) { \
- mParent.release(c); \
- } \
- }
-
- LogParentCmd(Exit);
+ LogCmd(Clear, clear);
+ LogCmd(GetBufSize, getLogSize);
+ LogCmd(SetBufSize, setLogSize);
+ LogCmd(GetBufSizeUsed, getLogSizeUsed);
+ LogCmd(GetStatistics, getStatistics);
+ LogCmd(GetPruneList, getPruneList);
+ LogCmd(SetPruneList, setPruneList);
+ LogCmd(GetEventTag, getEventTag);
+ LogCmd(Reinit, reinit);
+ LogCmd(Exit, EXIT);
+#undef LogCmd
};
-
-#endif
diff --git a/logd/FlushCommand.cpp b/logd/FlushCommand.cpp
deleted file mode 100644
index bd17555..0000000
--- a/logd/FlushCommand.cpp
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2012-2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdlib.h>
-
-#include <private/android_filesystem_config.h>
-
-#include "FlushCommand.h"
-#include "LogBuffer.h"
-#include "LogBufferElement.h"
-#include "LogCommand.h"
-#include "LogReader.h"
-#include "LogTimes.h"
-#include "LogUtils.h"
-
-// runSocketCommand is called once for every open client on the
-// log reader socket. Here we manage and associated the reader
-// client tracking and log region locks LastLogTimes list of
-// LogTimeEntrys, and spawn a transitory per-client thread to
-// work at filing data to the socket.
-//
-// global LogTimeEntry::wrlock() is used to protect access,
-// reference counts are used to ensure that individual
-// LogTimeEntry lifetime is managed when not protected.
-void FlushCommand::runSocketCommand(SocketClient* client) {
- LogTimeEntry* entry = nullptr;
- LastLogTimes& times = mReader.logbuf().mTimes;
-
- LogTimeEntry::wrlock();
- LastLogTimes::iterator it = times.begin();
- while (it != times.end()) {
- entry = it->get();
- if (entry->mClient == client) {
- if (!entry->isWatchingMultiple(mLogMask)) {
- LogTimeEntry::unlock();
- return;
- }
- if (entry->mTimeout.tv_sec || entry->mTimeout.tv_nsec) {
- if (mReader.logbuf().isMonotonic()) {
- LogTimeEntry::unlock();
- return;
- }
- // If the user changes the time in a gross manner that
- // invalidates the timeout, fall through and trigger.
- log_time now(CLOCK_REALTIME);
- if (((entry->mEnd + entry->mTimeout) > now) &&
- (now > entry->mEnd)) {
- LogTimeEntry::unlock();
- return;
- }
- }
- entry->triggerReader_Locked();
- LogTimeEntry::unlock();
- return;
- }
- it++;
- }
-
- LogTimeEntry::unlock();
-}
-
-bool FlushCommand::hasReadLogs(SocketClient* client) {
- return clientHasLogCredentials(client);
-}
-
-static bool clientHasSecurityCredentials(SocketClient* client) {
- return (client->getUid() == AID_SYSTEM) || (client->getGid() == AID_SYSTEM);
-}
-
-bool FlushCommand::hasSecurityLogs(SocketClient* client) {
- return clientHasSecurityCredentials(client);
-}
diff --git a/logd/FlushCommand.h b/logd/FlushCommand.h
deleted file mode 100644
index ceaf393..0000000
--- a/logd/FlushCommand.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2012-2013 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 _FLUSH_COMMAND_H
-#define _FLUSH_COMMAND_H
-
-#include <private/android_logger.h>
-#include <sysutils/SocketClientCommand.h>
-
-class LogBufferElement;
-
-#include "LogTimes.h"
-
-class LogReader;
-
-class FlushCommand : public SocketClientCommand {
- LogReader& mReader;
- log_mask_t mLogMask;
-
- public:
- explicit FlushCommand(LogReader& reader, log_mask_t logMask)
- : mReader(reader), mLogMask(logMask) {
- }
-
- virtual void runSocketCommand(SocketClient* client);
-
- static bool hasReadLogs(SocketClient* client);
- static bool hasSecurityLogs(SocketClient* client);
-};
-
-#endif
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index d9cc0db..0ce9796 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "LogAudit.h"
+
#include <ctype.h>
#include <endian.h>
#include <errno.h>
@@ -34,10 +36,7 @@
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
-#include "LogAudit.h"
-#include "LogBuffer.h"
#include "LogKlog.h"
-#include "LogReader.h"
#include "LogUtils.h"
#include "libaudit.h"
@@ -45,16 +44,14 @@
'<', '0' + LOG_MAKEPRI(LOG_AUTH, LOG_PRI(PRI)) / 10, \
'0' + LOG_MAKEPRI(LOG_AUTH, LOG_PRI(PRI)) % 10, '>'
-LogAudit::LogAudit(LogBuffer* buf, LogReader* reader, int fdDmesg)
+LogAudit::LogAudit(LogBuffer* buf, int fdDmesg, LogStatistics* stats)
: SocketListener(getLogSocket(), false),
logbuf(buf),
- reader(reader),
fdDmesg(fdDmesg),
- main(__android_logger_property_get_bool("ro.logd.auditd.main",
- BOOL_DEFAULT_TRUE)),
- events(__android_logger_property_get_bool("ro.logd.auditd.events",
- BOOL_DEFAULT_TRUE)),
- initialized(false) {
+ main(__android_logger_property_get_bool("ro.logd.auditd.main", BOOL_DEFAULT_TRUE)),
+ events(__android_logger_property_get_bool("ro.logd.auditd.events", BOOL_DEFAULT_TRUE)),
+ initialized(false),
+ stats_(stats) {
static const char auditd_message[] = { KMSG_PRIORITY(LOG_INFO),
'l',
'o',
@@ -211,9 +208,7 @@
++cp;
}
tid = pid;
- logbuf->wrlock();
- uid = logbuf->pidToUid(pid);
- logbuf->unlock();
+ uid = stats_->PidToUid(pid);
memmove(pidptr, cp, strlen(cp) + 1);
}
@@ -247,22 +242,10 @@
static const char audit_str[] = " audit(";
char* timeptr = strstr(str, audit_str);
- if (timeptr &&
- ((cp = now.strptime(timeptr + sizeof(audit_str) - 1, "%s.%q"))) &&
+ if (timeptr && ((cp = now.strptime(timeptr + sizeof(audit_str) - 1, "%s.%q"))) &&
(*cp == ':')) {
memcpy(timeptr + sizeof(audit_str) - 1, "0.0", 3);
memmove(timeptr + sizeof(audit_str) - 1 + 3, cp, strlen(cp) + 1);
- if (!isMonotonic()) {
- if (android::isMonotonic(now)) {
- LogKlog::convertMonotonicToReal(now);
- }
- } else {
- if (!android::isMonotonic(now)) {
- LogKlog::convertRealToMonotonic(now);
- }
- }
- } else if (isMonotonic()) {
- now = log_time(CLOCK_MONOTONIC);
} else {
now = log_time(CLOCK_REALTIME);
}
@@ -277,7 +260,7 @@
: LOGGER_ENTRY_MAX_PAYLOAD;
size_t message_len = str_len + sizeof(android_log_event_string_t);
- log_mask_t notify = 0;
+ unsigned int notify = 0;
if (events) { // begin scope for event buffer
uint32_t buffer[(message_len + sizeof(uint32_t) - 1) / sizeof(uint32_t)];
@@ -291,9 +274,8 @@
memcpy(event->data + str_len - denial_metadata.length(),
denial_metadata.c_str(), denial_metadata.length());
- rc = logbuf->log(
- LOG_ID_EVENTS, now, uid, pid, tid, reinterpret_cast<char*>(event),
- (message_len <= UINT16_MAX) ? (uint16_t)message_len : UINT16_MAX);
+ rc = logbuf->Log(LOG_ID_EVENTS, now, uid, pid, tid, reinterpret_cast<char*>(event),
+ (message_len <= UINT16_MAX) ? (uint16_t)message_len : UINT16_MAX);
if (rc >= 0) {
notify |= 1 << LOG_ID_EVENTS;
}
@@ -313,9 +295,7 @@
pid = tid;
comm = "auditd";
} else {
- logbuf->wrlock();
- comm = commfree = logbuf->pidToName(pid);
- logbuf->unlock();
+ comm = commfree = stats_->PidToName(pid);
if (!comm) {
comm = "unknown";
}
@@ -347,9 +327,8 @@
strncpy(newstr + 1 + str_len + prefix_len + suffix_len,
denial_metadata.c_str(), denial_metadata.length());
- rc = logbuf->log(
- LOG_ID_MAIN, now, uid, pid, tid, newstr,
- (message_len <= UINT16_MAX) ? (uint16_t)message_len : UINT16_MAX);
+ rc = logbuf->Log(LOG_ID_MAIN, now, uid, pid, tid, newstr,
+ (message_len <= UINT16_MAX) ? (uint16_t)message_len : UINT16_MAX);
if (rc >= 0) {
notify |= 1 << LOG_ID_MAIN;
@@ -361,7 +340,6 @@
free(str);
if (notify) {
- reader->notifyNewLog(notify);
if (rc < 0) {
rc = message_len;
}
diff --git a/logd/LogAudit.h b/logd/LogAudit.h
index c3d7a3e..181920e 100644
--- a/logd/LogAudit.h
+++ b/logd/LogAudit.h
@@ -14,36 +14,30 @@
* limitations under the License.
*/
-#ifndef _LOGD_LOG_AUDIT_H__
-#define _LOGD_LOG_AUDIT_H__
+#pragma once
#include <map>
#include <sysutils/SocketListener.h>
#include "LogBuffer.h"
-
-class LogReader;
+#include "LogStatistics.h"
class LogAudit : public SocketListener {
LogBuffer* logbuf;
- LogReader* reader;
int fdDmesg; // fdDmesg >= 0 is functionally bool dmesg
bool main;
bool events;
bool initialized;
- public:
- LogAudit(LogBuffer* buf, LogReader* reader, int fdDmesg);
+ public:
+ LogAudit(LogBuffer* buf, int fdDmesg, LogStatistics* stats);
int log(char* buf, size_t len);
- bool isMonotonic() {
- return logbuf->isMonotonic();
- }
- protected:
+ protected:
virtual bool onDataAvailable(SocketClient* cli);
- private:
+ private:
static int getLogSocket();
std::map<std::string, std::string> populateDenialMap();
std::string denialParse(const std::string& denial, char terminator,
@@ -51,6 +45,6 @@
void auditParse(const std::string& string, uid_t uid, std::string* bug_num);
int logPrint(const char* fmt, ...)
__attribute__((__format__(__printf__, 2, 3)));
-};
-#endif
+ LogStatistics* stats_;
+};
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
deleted file mode 100644
index 1cf2061..0000000
--- a/logd/LogBuffer.cpp
+++ /dev/null
@@ -1,1225 +0,0 @@
-/*
- * Copyright (C) 2012-2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// for manual checking of stale entries during LogBuffer::erase()
-//#define DEBUG_CHECK_FOR_STALE_ENTRIES
-
-#include <ctype.h>
-#include <endian.h>
-#include <errno.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/cdefs.h>
-#include <sys/user.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <unordered_map>
-
-#include <cutils/properties.h>
-#include <private/android_logger.h>
-
-#include "LogBuffer.h"
-#include "LogKlog.h"
-#include "LogReader.h"
-#include "LogUtils.h"
-
-#ifndef __predict_false
-#define __predict_false(exp) __builtin_expect((exp) != 0, 0)
-#endif
-
-// Default
-#define log_buffer_size(id) mMaxSize[id]
-
-const log_time LogBuffer::pruneMargin(3, 0);
-
-void LogBuffer::init() {
- log_id_for_each(i) {
- mLastSet[i] = false;
- mLast[i] = mLogElements.begin();
-
- if (setSize(i, __android_logger_get_buffer_size(i))) {
- setSize(i, LOG_BUFFER_MIN_SIZE);
- }
- }
- bool lastMonotonic = monotonic;
- monotonic = android_log_clockid() == CLOCK_MONOTONIC;
- if (lastMonotonic != monotonic) {
- //
- // Fixup all timestamps, may not be 100% accurate, but better than
- // throwing what we have away when we get 'surprised' by a change.
- // In-place element fixup so no need to check reader-lock. Entries
- // should already be in timestamp order, but we could end up with a
- // few out-of-order entries if new monotonics come in before we
- // are notified of the reinit change in status. A Typical example would
- // be:
- // --------- beginning of system
- // 10.494082 184 201 D Cryptfs : Just triggered post_fs_data
- // --------- beginning of kernel
- // 0.000000 0 0 I : Initializing cgroup subsys
- // as the act of mounting /data would trigger persist.logd.timestamp to
- // be corrected. 1/30 corner case YMMV.
- //
- rdlock();
- LogBufferElementCollection::iterator it = mLogElements.begin();
- while ((it != mLogElements.end())) {
- LogBufferElement* e = *it;
- if (monotonic) {
- if (!android::isMonotonic(e->mRealTime)) {
- LogKlog::convertRealToMonotonic(e->mRealTime);
- if ((e->mRealTime.tv_nsec % 1000) == 0) {
- e->mRealTime.tv_nsec++;
- }
- }
- } else {
- if (android::isMonotonic(e->mRealTime)) {
- LogKlog::convertMonotonicToReal(e->mRealTime);
- if ((e->mRealTime.tv_nsec % 1000) == 0) {
- e->mRealTime.tv_nsec++;
- }
- }
- }
- ++it;
- }
- unlock();
- }
-
- // We may have been triggered by a SIGHUP. Release any sleeping reader
- // threads to dump their current content.
- //
- // NB: this is _not_ performed in the context of a SIGHUP, it is
- // performed during startup, and in context of reinit administrative thread
- LogTimeEntry::wrlock();
-
- LastLogTimes::iterator times = mTimes.begin();
- while (times != mTimes.end()) {
- LogTimeEntry* entry = times->get();
- entry->triggerReader_Locked();
- times++;
- }
-
- LogTimeEntry::unlock();
-}
-
-LogBuffer::LogBuffer(LastLogTimes* times)
- : monotonic(android_log_clockid() == CLOCK_MONOTONIC), mTimes(*times) {
- pthread_rwlock_init(&mLogElementsLock, nullptr);
-
- log_id_for_each(i) {
- lastLoggedElements[i] = nullptr;
- droppedElements[i] = nullptr;
- }
-
- init();
-}
-
-LogBuffer::~LogBuffer() {
- log_id_for_each(i) {
- delete lastLoggedElements[i];
- delete droppedElements[i];
- }
-}
-
-enum match_type { DIFFERENT, SAME, SAME_LIBLOG };
-
-static enum match_type identical(LogBufferElement* elem,
- LogBufferElement* last) {
- // is it mostly identical?
- // if (!elem) return DIFFERENT;
- ssize_t lenl = elem->getMsgLen();
- if (lenl <= 0) return DIFFERENT; // value if this represents a chatty elem
- // if (!last) return DIFFERENT;
- ssize_t lenr = last->getMsgLen();
- if (lenr <= 0) return DIFFERENT; // value if this represents a chatty elem
- // if (elem->getLogId() != last->getLogId()) return DIFFERENT;
- if (elem->getUid() != last->getUid()) return DIFFERENT;
- if (elem->getPid() != last->getPid()) return DIFFERENT;
- if (elem->getTid() != last->getTid()) return DIFFERENT;
-
- // last is more than a minute old, stop squashing identical messages
- if (elem->getRealTime().nsec() >
- (last->getRealTime().nsec() + 60 * NS_PER_SEC))
- return DIFFERENT;
-
- // Identical message
- const char* msgl = elem->getMsg();
- const char* msgr = last->getMsg();
- if (lenl == lenr) {
- if (!fastcmp<memcmp>(msgl, msgr, lenl)) return SAME;
- // liblog tagged messages (content gets summed)
- if ((elem->getLogId() == LOG_ID_EVENTS) &&
- (lenl == sizeof(android_log_event_int_t)) &&
- !fastcmp<memcmp>(msgl, msgr, sizeof(android_log_event_int_t) -
- sizeof(int32_t)) &&
- (elem->getTag() == LIBLOG_LOG_TAG)) {
- return SAME_LIBLOG;
- }
- }
-
- // audit message (except sequence number) identical?
- if (last->isBinary() &&
- (lenl > static_cast<ssize_t>(sizeof(android_log_event_string_t))) &&
- (lenr > static_cast<ssize_t>(sizeof(android_log_event_string_t)))) {
- if (fastcmp<memcmp>(msgl, msgr, sizeof(android_log_event_string_t) -
- sizeof(int32_t))) {
- return DIFFERENT;
- }
- msgl += sizeof(android_log_event_string_t);
- lenl -= sizeof(android_log_event_string_t);
- msgr += sizeof(android_log_event_string_t);
- lenr -= sizeof(android_log_event_string_t);
- }
- static const char avc[] = "): avc: ";
- const char* avcl = android::strnstr(msgl, lenl, avc);
- if (!avcl) return DIFFERENT;
- lenl -= avcl - msgl;
- const char* avcr = android::strnstr(msgr, lenr, avc);
- if (!avcr) return DIFFERENT;
- lenr -= avcr - msgr;
- if (lenl != lenr) return DIFFERENT;
- if (fastcmp<memcmp>(avcl + strlen(avc), avcr + strlen(avc),
- lenl - strlen(avc))) {
- return DIFFERENT;
- }
- return SAME;
-}
-
-int LogBuffer::log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid,
- pid_t tid, const char* msg, uint16_t len) {
- if (log_id >= LOG_ID_MAX) {
- return -EINVAL;
- }
-
- // Slip the time by 1 nsec if the incoming lands on xxxxxx000 ns.
- // This prevents any chance that an outside source can request an
- // exact entry with time specified in ms or us precision.
- if ((realtime.tv_nsec % 1000) == 0) ++realtime.tv_nsec;
-
- LogBufferElement* elem = new LogBufferElement(log_id, realtime, uid, pid, tid, msg, len);
-
- // b/137093665: don't coalesce security messages.
- if (log_id == LOG_ID_SECURITY) {
- wrlock();
- log(elem);
- unlock();
-
- return len;
- }
-
- int prio = ANDROID_LOG_INFO;
- const char* tag = nullptr;
- size_t tag_len = 0;
- if (log_id == LOG_ID_EVENTS || log_id == LOG_ID_STATS) {
- tag = tagToName(elem->getTag());
- if (tag) {
- tag_len = strlen(tag);
- }
- } else {
- prio = *msg;
- tag = msg + 1;
- tag_len = strnlen(tag, len - 1);
- }
- if (!__android_log_is_loggable_len(prio, tag, tag_len, ANDROID_LOG_VERBOSE)) {
- // Log traffic received to total
- wrlock();
- stats.addTotal(elem);
- unlock();
- delete elem;
- return -EACCES;
- }
-
- wrlock();
- LogBufferElement* currentLast = lastLoggedElements[log_id];
- if (currentLast) {
- LogBufferElement* dropped = droppedElements[log_id];
- uint16_t count = dropped ? dropped->getDropped() : 0;
- //
- // State Init
- // incoming:
- // dropped = nullptr
- // currentLast = nullptr;
- // elem = incoming message
- // outgoing:
- // dropped = nullptr -> State 0
- // currentLast = copy of elem
- // log elem
- // State 0
- // incoming:
- // count = 0
- // dropped = nullptr
- // currentLast = copy of last message
- // elem = incoming message
- // outgoing: if match != DIFFERENT
- // dropped = copy of first identical message -> State 1
- // currentLast = reference to elem
- // break: if match == DIFFERENT
- // dropped = nullptr -> State 0
- // delete copy of last message (incoming currentLast)
- // currentLast = copy of elem
- // log elem
- // State 1
- // incoming:
- // count = 0
- // dropped = copy of first identical message
- // currentLast = reference to last held-back incoming
- // message
- // elem = incoming message
- // outgoing: if match == SAME
- // delete copy of first identical message (dropped)
- // dropped = reference to last held-back incoming
- // message set to chatty count of 1 -> State 2
- // currentLast = reference to elem
- // outgoing: if match == SAME_LIBLOG
- // dropped = copy of first identical message -> State 1
- // take sum of currentLast and elem
- // if sum overflows:
- // log currentLast
- // currentLast = reference to elem
- // else
- // delete currentLast
- // currentLast = reference to elem, sum liblog.
- // break: if match == DIFFERENT
- // delete dropped
- // dropped = nullptr -> State 0
- // log reference to last held-back (currentLast)
- // currentLast = copy of elem
- // log elem
- // State 2
- // incoming:
- // count = chatty count
- // dropped = chatty message holding count
- // currentLast = reference to last held-back incoming
- // message.
- // dropped = chatty message holding count
- // elem = incoming message
- // outgoing: if match != DIFFERENT
- // delete chatty message holding count
- // dropped = reference to last held-back incoming
- // message, set to chatty count + 1
- // currentLast = reference to elem
- // break: if match == DIFFERENT
- // log dropped (chatty message)
- // dropped = nullptr -> State 0
- // log reference to last held-back (currentLast)
- // currentLast = copy of elem
- // log elem
- //
- enum match_type match = identical(elem, currentLast);
- if (match != DIFFERENT) {
- if (dropped) {
- // Sum up liblog tag messages?
- if ((count == 0) /* at Pass 1 */ && (match == SAME_LIBLOG)) {
- android_log_event_int_t* event =
- reinterpret_cast<android_log_event_int_t*>(
- const_cast<char*>(currentLast->getMsg()));
- //
- // To unit test, differentiate with something like:
- // event->header.tag = htole32(CHATTY_LOG_TAG);
- // here, then instead of delete currentLast below,
- // log(currentLast) to see the incremental sums form.
- //
- uint32_t swab = event->payload.data;
- unsigned long long total = htole32(swab);
- event = reinterpret_cast<android_log_event_int_t*>(
- const_cast<char*>(elem->getMsg()));
- swab = event->payload.data;
-
- lastLoggedElements[LOG_ID_EVENTS] = elem;
- total += htole32(swab);
- // check for overflow
- if (total >= UINT32_MAX) {
- log(currentLast);
- unlock();
- return len;
- }
- stats.addTotal(currentLast);
- delete currentLast;
- swab = total;
- event->payload.data = htole32(swab);
- unlock();
- return len;
- }
- if (count == USHRT_MAX) {
- log(dropped);
- count = 1;
- } else {
- delete dropped;
- ++count;
- }
- }
- if (count) {
- stats.addTotal(currentLast);
- currentLast->setDropped(count);
- }
- droppedElements[log_id] = currentLast;
- lastLoggedElements[log_id] = elem;
- unlock();
- return len;
- }
- if (dropped) { // State 1 or 2
- if (count) { // State 2
- log(dropped); // report chatty
- } else { // State 1
- delete dropped;
- }
- droppedElements[log_id] = nullptr;
- log(currentLast); // report last message in the series
- } else { // State 0
- delete currentLast;
- }
- }
- lastLoggedElements[log_id] = new LogBufferElement(*elem);
-
- log(elem);
- unlock();
-
- return len;
-}
-
-// assumes LogBuffer::wrlock() held, owns elem, look after garbage collection
-void LogBuffer::log(LogBufferElement* elem) {
- // cap on how far back we will sort in-place, otherwise append
- static uint32_t too_far_back = 5; // five seconds
- // Insert elements in time sorted order if possible
- // NB: if end is region locked, place element at end of list
- LogBufferElementCollection::iterator it = mLogElements.end();
- LogBufferElementCollection::iterator last = it;
- if (__predict_true(it != mLogElements.begin())) --it;
- if (__predict_false(it == mLogElements.begin()) ||
- __predict_true((*it)->getRealTime() <= elem->getRealTime()) ||
- __predict_false((((*it)->getRealTime().tv_sec - too_far_back) >
- elem->getRealTime().tv_sec) &&
- (elem->getLogId() != LOG_ID_KERNEL) &&
- ((*it)->getLogId() != LOG_ID_KERNEL))) {
- mLogElements.push_back(elem);
- } else {
- log_time end(log_time::EPOCH);
- bool end_set = false;
- bool end_always = false;
-
- LogTimeEntry::rdlock();
-
- LastLogTimes::iterator times = mTimes.begin();
- while (times != mTimes.end()) {
- LogTimeEntry* entry = times->get();
- if (!entry->mNonBlock) {
- end_always = true;
- break;
- }
- // it passing mEnd is blocked by the following checks.
- if (!end_set || (end <= entry->mEnd)) {
- end = entry->mEnd;
- end_set = true;
- }
- times++;
- }
-
- if (end_always || (end_set && (end > (*it)->getRealTime()))) {
- mLogElements.push_back(elem);
- } else {
- // should be short as timestamps are localized near end()
- do {
- last = it;
- if (__predict_false(it == mLogElements.begin())) {
- break;
- }
- --it;
- } while (((*it)->getRealTime() > elem->getRealTime()) &&
- (!end_set || (end <= (*it)->getRealTime())));
- mLogElements.insert(last, elem);
- }
- LogTimeEntry::unlock();
- }
-
- stats.add(elem);
- maybePrune(elem->getLogId());
-}
-
-// Prune at most 10% of the log entries or maxPrune, whichever is less.
-//
-// LogBuffer::wrlock() must be held when this function is called.
-void LogBuffer::maybePrune(log_id_t id) {
- size_t sizes = stats.sizes(id);
- unsigned long maxSize = log_buffer_size(id);
- if (sizes > maxSize) {
- size_t sizeOver = sizes - ((maxSize * 9) / 10);
- size_t elements = stats.realElements(id);
- size_t minElements = elements / 100;
- if (minElements < minPrune) {
- minElements = minPrune;
- }
- unsigned long pruneRows = elements * sizeOver / sizes;
- if (pruneRows < minElements) {
- pruneRows = minElements;
- }
- if (pruneRows > maxPrune) {
- pruneRows = maxPrune;
- }
- prune(id, pruneRows);
- }
-}
-
-LogBufferElementCollection::iterator LogBuffer::erase(
- LogBufferElementCollection::iterator it, bool coalesce) {
- LogBufferElement* element = *it;
- log_id_t id = element->getLogId();
-
- // Remove iterator references in the various lists that will become stale
- // after the element is erased from the main logging list.
-
- { // start of scope for found iterator
- int key = ((id == LOG_ID_EVENTS) || (id == LOG_ID_SECURITY))
- ? element->getTag()
- : element->getUid();
- LogBufferIteratorMap::iterator found = mLastWorst[id].find(key);
- if ((found != mLastWorst[id].end()) && (it == found->second)) {
- mLastWorst[id].erase(found);
- }
- }
-
- { // start of scope for pid found iterator
- // element->getUid() may not be AID_SYSTEM for next-best-watermark.
- // will not assume id != LOG_ID_EVENTS or LOG_ID_SECURITY for KISS and
- // long term code stability, find() check should be fast for those ids.
- LogBufferPidIteratorMap::iterator found =
- mLastWorstPidOfSystem[id].find(element->getPid());
- if ((found != mLastWorstPidOfSystem[id].end()) &&
- (it == found->second)) {
- mLastWorstPidOfSystem[id].erase(found);
- }
- }
-
- bool setLast[LOG_ID_MAX];
- bool doSetLast = false;
- log_id_for_each(i) {
- doSetLast |= setLast[i] = mLastSet[i] && (it == mLast[i]);
- }
-#ifdef DEBUG_CHECK_FOR_STALE_ENTRIES
- LogBufferElementCollection::iterator bad = it;
- int key = ((id == LOG_ID_EVENTS) || (id == LOG_ID_SECURITY))
- ? element->getTag()
- : element->getUid();
-#endif
- it = mLogElements.erase(it);
- if (doSetLast) {
- log_id_for_each(i) {
- if (setLast[i]) {
- if (__predict_false(it == mLogElements.end())) { // impossible
- mLastSet[i] = false;
- mLast[i] = mLogElements.begin();
- } else {
- mLast[i] = it; // push down the road as next-best-watermark
- }
- }
- }
- }
-#ifdef DEBUG_CHECK_FOR_STALE_ENTRIES
- log_id_for_each(i) {
- for (auto b : mLastWorst[i]) {
- if (bad == b.second) {
- android::prdebug("stale mLastWorst[%d] key=%d mykey=%d\n", i,
- b.first, key);
- }
- }
- for (auto b : mLastWorstPidOfSystem[i]) {
- if (bad == b.second) {
- android::prdebug("stale mLastWorstPidOfSystem[%d] pid=%d\n", i,
- b.first);
- }
- }
- if (mLastSet[i] && (bad == mLast[i])) {
- android::prdebug("stale mLast[%d]\n", i);
- mLastSet[i] = false;
- mLast[i] = mLogElements.begin();
- }
- }
-#endif
- if (coalesce) {
- stats.erase(element);
- } else {
- stats.subtract(element);
- }
- delete element;
-
- return it;
-}
-
-// Define a temporary mechanism to report the last LogBufferElement pointer
-// for the specified uid, pid and tid. Used below to help merge-sort when
-// pruning for worst UID.
-class LogBufferElementKey {
- const union {
- struct {
- uint32_t uid;
- uint16_t pid;
- uint16_t tid;
- } __packed;
- uint64_t value;
- } __packed;
-
- public:
- LogBufferElementKey(uid_t uid, pid_t pid, pid_t tid)
- : uid(uid), pid(pid), tid(tid) {
- }
- explicit LogBufferElementKey(uint64_t key) : value(key) {
- }
-
- uint64_t getKey() {
- return value;
- }
-};
-
-class LogBufferElementLast {
- typedef std::unordered_map<uint64_t, LogBufferElement*> LogBufferElementMap;
- LogBufferElementMap map;
-
- public:
- bool coalesce(LogBufferElement* element, uint16_t dropped) {
- LogBufferElementKey key(element->getUid(), element->getPid(),
- element->getTid());
- LogBufferElementMap::iterator it = map.find(key.getKey());
- if (it != map.end()) {
- LogBufferElement* found = it->second;
- uint16_t moreDropped = found->getDropped();
- if ((dropped + moreDropped) > USHRT_MAX) {
- map.erase(it);
- } else {
- found->setDropped(dropped + moreDropped);
- return true;
- }
- }
- return false;
- }
-
- void add(LogBufferElement* element) {
- LogBufferElementKey key(element->getUid(), element->getPid(),
- element->getTid());
- map[key.getKey()] = element;
- }
-
- inline void clear() {
- map.clear();
- }
-
- void clear(LogBufferElement* element) {
- log_time current =
- element->getRealTime() - log_time(EXPIRE_RATELIMIT, 0);
- for (LogBufferElementMap::iterator it = map.begin(); it != map.end();) {
- LogBufferElement* mapElement = it->second;
- if ((mapElement->getDropped() >= EXPIRE_THRESHOLD) &&
- (current > mapElement->getRealTime())) {
- it = map.erase(it);
- } else {
- ++it;
- }
- }
- }
-};
-
-// Determine if watermark is within pruneMargin + 1s from the end of the list,
-// the caller will use this result to set an internal busy flag indicating
-// the prune operation could not be completed because a reader is blocking
-// the request.
-bool LogBuffer::isBusy(log_time watermark) {
- LogBufferElementCollection::iterator ei = mLogElements.end();
- --ei;
- return watermark < ((*ei)->getRealTime() - pruneMargin - log_time(1, 0));
-}
-
-// If the selected reader is blocking our pruning progress, decide on
-// what kind of mitigation is necessary to unblock the situation.
-void LogBuffer::kickMe(LogTimeEntry* me, log_id_t id, unsigned long pruneRows) {
- if (stats.sizes(id) > (2 * log_buffer_size(id))) { // +100%
- // A misbehaving or slow reader has its connection
- // dropped if we hit too much memory pressure.
- android::prdebug("Kicking blocked reader, pid %d, from LogBuffer::kickMe()\n",
- me->mClient->getPid());
- me->release_Locked();
- } else if (me->mTimeout.tv_sec || me->mTimeout.tv_nsec) {
- // Allow a blocked WRAP timeout reader to
- // trigger and start reporting the log data.
- me->triggerReader_Locked();
- } else {
- // tell slow reader to skip entries to catch up
- android::prdebug(
- "Skipping %lu entries from slow reader, pid %d, from LogBuffer::kickMe()\n",
- pruneRows, me->mClient->getPid());
- me->triggerSkip_Locked(id, pruneRows);
- }
-}
-
-// prune "pruneRows" of type "id" from the buffer.
-//
-// This garbage collection task is used to expire log entries. It is called to
-// remove all logs (clear), all UID logs (unprivileged clear), or every
-// 256 or 10% of the total logs (whichever is less) to prune the logs.
-//
-// First there is a prep phase where we discover the reader region lock that
-// acts as a backstop to any pruning activity to stop there and go no further.
-//
-// There are three major pruning loops that follow. All expire from the oldest
-// entries. Since there are multiple log buffers, the Android logging facility
-// will appear to drop entries 'in the middle' when looking at multiple log
-// sources and buffers. This effect is slightly more prominent when we prune
-// the worst offender by logging source. Thus the logs slowly loose content
-// and value as you move back in time. This is preferred since chatty sources
-// invariably move the logs value down faster as less chatty sources would be
-// expired in the noise.
-//
-// The first loop performs blacklisting and worst offender pruning. Falling
-// through when there are no notable worst offenders and have not hit the
-// region lock preventing further worst offender pruning. This loop also looks
-// after managing the chatty log entries and merging to help provide
-// statistical basis for blame. The chatty entries are not a notification of
-// how much logs you may have, but instead represent how much logs you would
-// have had in a virtual log buffer that is extended to cover all the in-memory
-// logs without loss. They last much longer than the represented pruned logs
-// since they get multiplied by the gains in the non-chatty log sources.
-//
-// The second loop get complicated because an algorithm of watermarks and
-// history is maintained to reduce the order and keep processing time
-// down to a minimum at scale. These algorithms can be costly in the face
-// of larger log buffers, or severly limited processing time granted to a
-// background task at lowest priority.
-//
-// This second loop does straight-up expiration from the end of the logs
-// (again, remember for the specified log buffer id) but does some whitelist
-// preservation. Thus whitelist is a Hail Mary low priority, blacklists and
-// spam filtration all take priority. This second loop also checks if a region
-// lock is causing us to buffer too much in the logs to help the reader(s),
-// and will tell the slowest reader thread to skip log entries, and if
-// persistent and hits a further threshold, kill the reader thread.
-//
-// The third thread is optional, and only gets hit if there was a whitelist
-// and more needs to be pruned against the backstop of the region lock.
-//
-// LogBuffer::wrlock() must be held when this function is called.
-//
-bool LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
- LogTimeEntry* oldest = nullptr;
- bool busy = false;
- bool clearAll = pruneRows == ULONG_MAX;
-
- LogTimeEntry::rdlock();
-
- // Region locked?
- LastLogTimes::iterator times = mTimes.begin();
- while (times != mTimes.end()) {
- LogTimeEntry* entry = times->get();
- if (entry->isWatching(id) &&
- (!oldest || (oldest->mStart > entry->mStart) ||
- ((oldest->mStart == entry->mStart) &&
- (entry->mTimeout.tv_sec || entry->mTimeout.tv_nsec)))) {
- oldest = entry;
- }
- times++;
- }
- log_time watermark(log_time::tv_sec_max, log_time::tv_nsec_max);
- if (oldest) watermark = oldest->mStart - pruneMargin;
-
- LogBufferElementCollection::iterator it;
-
- if (__predict_false(caller_uid != AID_ROOT)) { // unlikely
- // Only here if clear all request from non system source, so chatty
- // filter logistics is not required.
- it = mLastSet[id] ? mLast[id] : mLogElements.begin();
- while (it != mLogElements.end()) {
- LogBufferElement* element = *it;
-
- if ((element->getLogId() != id) ||
- (element->getUid() != caller_uid)) {
- ++it;
- continue;
- }
-
- if (!mLastSet[id] || ((*mLast[id])->getLogId() != id)) {
- mLast[id] = it;
- mLastSet[id] = true;
- }
-
- if (oldest && (watermark <= element->getRealTime())) {
- busy = isBusy(watermark);
- if (busy) kickMe(oldest, id, pruneRows);
- break;
- }
-
- it = erase(it);
- if (--pruneRows == 0) {
- break;
- }
- }
- LogTimeEntry::unlock();
- return busy;
- }
-
- // prune by worst offenders; by blacklist, UID, and by PID of system UID
- bool hasBlacklist = (id != LOG_ID_SECURITY) && mPrune.naughty();
- while (!clearAll && (pruneRows > 0)) {
- // recalculate the worst offender on every batched pass
- int worst = -1; // not valid for getUid() or getKey()
- size_t worst_sizes = 0;
- size_t second_worst_sizes = 0;
- pid_t worstPid = 0; // POSIX guarantees PID != 0
-
- if (worstUidEnabledForLogid(id) && mPrune.worstUidEnabled()) {
- // Calculate threshold as 12.5% of available storage
- size_t threshold = log_buffer_size(id) / 8;
-
- if ((id == LOG_ID_EVENTS) || (id == LOG_ID_SECURITY)) {
- stats.sortTags(AID_ROOT, (pid_t)0, 2, id)
- .findWorst(worst, worst_sizes, second_worst_sizes,
- threshold);
- // per-pid filter for AID_SYSTEM sources is too complex
- } else {
- stats.sort(AID_ROOT, (pid_t)0, 2, id)
- .findWorst(worst, worst_sizes, second_worst_sizes,
- threshold);
-
- if ((worst == AID_SYSTEM) && mPrune.worstPidOfSystemEnabled()) {
- stats.sortPids(worst, (pid_t)0, 2, id)
- .findWorst(worstPid, worst_sizes, second_worst_sizes);
- }
- }
- }
-
- // skip if we have neither worst nor naughty filters
- if ((worst == -1) && !hasBlacklist) {
- break;
- }
-
- bool kick = false;
- bool leading = true;
- it = mLastSet[id] ? mLast[id] : mLogElements.begin();
- // Perform at least one mandatory garbage collection cycle in following
- // - clear leading chatty tags
- // - coalesce chatty tags
- // - check age-out of preserved logs
- bool gc = pruneRows <= 1;
- if (!gc && (worst != -1)) {
- { // begin scope for worst found iterator
- LogBufferIteratorMap::iterator found =
- mLastWorst[id].find(worst);
- if ((found != mLastWorst[id].end()) &&
- (found->second != mLogElements.end())) {
- leading = false;
- it = found->second;
- }
- }
- if (worstPid) { // begin scope for pid worst found iterator
- // FYI: worstPid only set if !LOG_ID_EVENTS and
- // !LOG_ID_SECURITY, not going to make that assumption ...
- LogBufferPidIteratorMap::iterator found =
- mLastWorstPidOfSystem[id].find(worstPid);
- if ((found != mLastWorstPidOfSystem[id].end()) &&
- (found->second != mLogElements.end())) {
- leading = false;
- it = found->second;
- }
- }
- }
- static const timespec too_old = { EXPIRE_HOUR_THRESHOLD * 60 * 60, 0 };
- LogBufferElementCollection::iterator lastt;
- lastt = mLogElements.end();
- --lastt;
- LogBufferElementLast last;
- while (it != mLogElements.end()) {
- LogBufferElement* element = *it;
-
- if (oldest && (watermark <= element->getRealTime())) {
- busy = isBusy(watermark);
- // Do not let chatty eliding trigger any reader mitigation
- break;
- }
-
- if (element->getLogId() != id) {
- ++it;
- continue;
- }
- // below this point element->getLogId() == id
-
- if (leading && (!mLastSet[id] || ((*mLast[id])->getLogId() != id))) {
- mLast[id] = it;
- mLastSet[id] = true;
- }
-
- uint16_t dropped = element->getDropped();
-
- // remove any leading drops
- if (leading && dropped) {
- it = erase(it);
- continue;
- }
-
- if (dropped && last.coalesce(element, dropped)) {
- it = erase(it, true);
- continue;
- }
-
- int key = ((id == LOG_ID_EVENTS) || (id == LOG_ID_SECURITY))
- ? element->getTag()
- : element->getUid();
-
- if (hasBlacklist && mPrune.naughty(element)) {
- last.clear(element);
- it = erase(it);
- if (dropped) {
- continue;
- }
-
- pruneRows--;
- if (pruneRows == 0) {
- break;
- }
-
- if (key == worst) {
- kick = true;
- if (worst_sizes < second_worst_sizes) {
- break;
- }
- worst_sizes -= element->getMsgLen();
- }
- continue;
- }
-
- if ((element->getRealTime() < ((*lastt)->getRealTime() - too_old)) ||
- (element->getRealTime() > (*lastt)->getRealTime())) {
- break;
- }
-
- if (dropped) {
- last.add(element);
- if (worstPid &&
- ((!gc && (element->getPid() == worstPid)) ||
- (mLastWorstPidOfSystem[id].find(element->getPid()) ==
- mLastWorstPidOfSystem[id].end()))) {
- // element->getUid() may not be AID_SYSTEM, next best
- // watermark if current one empty. id is not LOG_ID_EVENTS
- // or LOG_ID_SECURITY because of worstPid check.
- mLastWorstPidOfSystem[id][element->getPid()] = it;
- }
- if ((!gc && !worstPid && (key == worst)) ||
- (mLastWorst[id].find(key) == mLastWorst[id].end())) {
- mLastWorst[id][key] = it;
- }
- ++it;
- continue;
- }
-
- if ((key != worst) ||
- (worstPid && (element->getPid() != worstPid))) {
- leading = false;
- last.clear(element);
- ++it;
- continue;
- }
- // key == worst below here
- // If worstPid set, then element->getPid() == worstPid below here
-
- pruneRows--;
- if (pruneRows == 0) {
- break;
- }
-
- kick = true;
-
- uint16_t len = element->getMsgLen();
-
- // do not create any leading drops
- if (leading) {
- it = erase(it);
- } else {
- stats.drop(element);
- element->setDropped(1);
- if (last.coalesce(element, 1)) {
- it = erase(it, true);
- } else {
- last.add(element);
- if (worstPid &&
- (!gc || (mLastWorstPidOfSystem[id].find(worstPid) ==
- mLastWorstPidOfSystem[id].end()))) {
- // element->getUid() may not be AID_SYSTEM, next best
- // watermark if current one empty. id is not
- // LOG_ID_EVENTS or LOG_ID_SECURITY because of worstPid.
- mLastWorstPidOfSystem[id][worstPid] = it;
- }
- if ((!gc && !worstPid) ||
- (mLastWorst[id].find(worst) == mLastWorst[id].end())) {
- mLastWorst[id][worst] = it;
- }
- ++it;
- }
- }
- if (worst_sizes < second_worst_sizes) {
- break;
- }
- worst_sizes -= len;
- }
- last.clear();
-
- if (!kick || !mPrune.worstUidEnabled()) {
- break; // the following loop will ask bad clients to skip/drop
- }
- }
-
- bool whitelist = false;
- bool hasWhitelist = (id != LOG_ID_SECURITY) && mPrune.nice() && !clearAll;
- it = mLastSet[id] ? mLast[id] : mLogElements.begin();
- while ((pruneRows > 0) && (it != mLogElements.end())) {
- LogBufferElement* element = *it;
-
- if (element->getLogId() != id) {
- it++;
- continue;
- }
-
- if (!mLastSet[id] || ((*mLast[id])->getLogId() != id)) {
- mLast[id] = it;
- mLastSet[id] = true;
- }
-
- if (oldest && (watermark <= element->getRealTime())) {
- busy = isBusy(watermark);
- if (!whitelist && busy) kickMe(oldest, id, pruneRows);
- break;
- }
-
- if (hasWhitelist && !element->getDropped() && mPrune.nice(element)) {
- // WhiteListed
- whitelist = true;
- it++;
- continue;
- }
-
- it = erase(it);
- pruneRows--;
- }
-
- // Do not save the whitelist if we are reader range limited
- if (whitelist && (pruneRows > 0)) {
- it = mLastSet[id] ? mLast[id] : mLogElements.begin();
- while ((it != mLogElements.end()) && (pruneRows > 0)) {
- LogBufferElement* element = *it;
-
- if (element->getLogId() != id) {
- ++it;
- continue;
- }
-
- if (!mLastSet[id] || ((*mLast[id])->getLogId() != id)) {
- mLast[id] = it;
- mLastSet[id] = true;
- }
-
- if (oldest && (watermark <= element->getRealTime())) {
- busy = isBusy(watermark);
- if (busy) kickMe(oldest, id, pruneRows);
- break;
- }
-
- it = erase(it);
- pruneRows--;
- }
- }
-
- LogTimeEntry::unlock();
-
- return (pruneRows > 0) && busy;
-}
-
-// clear all rows of type "id" from the buffer.
-bool LogBuffer::clear(log_id_t id, uid_t uid) {
- bool busy = true;
- // If it takes more than 4 tries (seconds) to clear, then kill reader(s)
- for (int retry = 4;;) {
- if (retry == 1) { // last pass
- // Check if it is still busy after the sleep, we say prune
- // one entry, not another clear run, so we are looking for
- // the quick side effect of the return value to tell us if
- // we have a _blocked_ reader.
- wrlock();
- busy = prune(id, 1, uid);
- unlock();
- // It is still busy, blocked reader(s), lets kill them all!
- // otherwise, lets be a good citizen and preserve the slow
- // readers and let the clear run (below) deal with determining
- // if we are still blocked and return an error code to caller.
- if (busy) {
- LogTimeEntry::wrlock();
- LastLogTimes::iterator times = mTimes.begin();
- while (times != mTimes.end()) {
- LogTimeEntry* entry = times->get();
- // Killer punch
- if (entry->isWatching(id)) {
- android::prdebug(
- "Kicking blocked reader, pid %d, from LogBuffer::clear()\n",
- entry->mClient->getPid());
- entry->release_Locked();
- }
- times++;
- }
- LogTimeEntry::unlock();
- }
- }
- wrlock();
- busy = prune(id, ULONG_MAX, uid);
- unlock();
- if (!busy || !--retry) {
- break;
- }
- sleep(1); // Let reader(s) catch up after notification
- }
- return busy;
-}
-
-// get the used space associated with "id".
-unsigned long LogBuffer::getSizeUsed(log_id_t id) {
- rdlock();
- size_t retval = stats.sizes(id);
- unlock();
- return retval;
-}
-
-// set the total space allocated to "id"
-int LogBuffer::setSize(log_id_t id, unsigned long size) {
- // Reasonable limits ...
- if (!__android_logger_valid_buffer_size(size)) {
- return -1;
- }
- wrlock();
- log_buffer_size(id) = size;
- unlock();
- return 0;
-}
-
-// get the total space allocated to "id"
-unsigned long LogBuffer::getSize(log_id_t id) {
- rdlock();
- size_t retval = log_buffer_size(id);
- unlock();
- return retval;
-}
-
-log_time LogBuffer::flushTo(SocketClient* reader, const log_time& start,
- pid_t* lastTid, bool privileged, bool security,
- int (*filter)(const LogBufferElement* element,
- void* arg),
- void* arg) {
- LogBufferElementCollection::iterator it;
- uid_t uid = reader->getUid();
-
- rdlock();
-
- if (start == log_time::EPOCH) {
- // client wants to start from the beginning
- it = mLogElements.begin();
- } else {
- // Cap to 300 iterations we look back for out-of-order entries.
- size_t count = 300;
-
- // Client wants to start from some specified time. Chances are
- // we are better off starting from the end of the time sorted list.
- LogBufferElementCollection::iterator last;
- for (last = it = mLogElements.end(); it != mLogElements.begin();
- /* do nothing */) {
- --it;
- LogBufferElement* element = *it;
- if (element->getRealTime() > start) {
- last = it;
- } else if (element->getRealTime() == start) {
- last = ++it;
- break;
- } else if (!--count) {
- break;
- }
- }
- it = last;
- }
-
- log_time curr = start;
-
- LogBufferElement* lastElement = nullptr; // iterator corruption paranoia
- static const size_t maxSkip = 4194304; // maximum entries to skip
- size_t skip = maxSkip;
- for (; it != mLogElements.end(); ++it) {
- LogBufferElement* element = *it;
-
- if (!--skip) {
- android::prdebug("reader.per: too many elements skipped");
- break;
- }
- if (element == lastElement) {
- android::prdebug("reader.per: identical elements");
- break;
- }
- lastElement = element;
-
- if (!privileged && (element->getUid() != uid)) {
- continue;
- }
-
- if (!security && (element->getLogId() == LOG_ID_SECURITY)) {
- continue;
- }
-
- // NB: calling out to another object with wrlock() held (safe)
- if (filter) {
- int ret = (*filter)(element, arg);
- if (ret == false) {
- continue;
- }
- if (ret != true) {
- break;
- }
- }
-
- bool sameTid = false;
- if (lastTid) {
- sameTid = lastTid[element->getLogId()] == element->getTid();
- // Dropped (chatty) immediately following a valid log from the
- // same source in the same log buffer indicates we have a
- // multiple identical squash. chatty that differs source
- // is due to spam filter. chatty to chatty of different
- // source is also due to spam filter.
- lastTid[element->getLogId()] =
- (element->getDropped() && !sameTid) ? 0 : element->getTid();
- }
-
- unlock();
-
- // range locking in LastLogTimes looks after us
- curr = element->flushTo(reader, this, sameTid);
-
- if (curr == element->FLUSH_ERROR) {
- return curr;
- }
-
- skip = maxSkip;
- rdlock();
- }
- unlock();
-
- return curr;
-}
-
-std::string LogBuffer::formatStatistics(uid_t uid, pid_t pid,
- unsigned int logMask) {
- wrlock();
-
- std::string ret = stats.format(uid, pid, logMask);
-
- unlock();
-
- return ret;
-}
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index c2d5b97..c5d333a 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2014 The Android Open Source Project
+ * 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.
@@ -14,176 +14,62 @@
* limitations under the License.
*/
-#ifndef _LOGD_LOG_BUFFER_H__
-#define _LOGD_LOG_BUFFER_H__
+#pragma once
#include <sys/types.h>
-#include <list>
-#include <string>
+#include <functional>
-#include <android/log.h>
-#include <private/android_filesystem_config.h>
-#include <sysutils/SocketClient.h>
+#include <log/log.h>
+#include <log/log_read.h>
-#include "LogBufferElement.h"
-#include "LogStatistics.h"
-#include "LogTags.h"
-#include "LogTimes.h"
-#include "LogWhiteBlackList.h"
+#include "LogWriter.h"
-//
-// We are either in 1970ish (MONOTONIC) or 2016+ish (REALTIME) so to
-// differentiate without prejudice, we use 1972 to delineate, earlier
-// is likely monotonic, later is real. Otherwise we start using a
-// dividing line between monotonic and realtime if more than a minute
-// difference between them.
-//
-namespace android {
+// A mask to represent which log buffers a reader is watching, values are (1 << LOG_ID_MAIN), etc.
+using LogMask = uint32_t;
+constexpr uint32_t kLogMaskAll = 0xFFFFFFFF;
-static bool isMonotonic(const log_time& mono) {
- static const uint32_t EPOCH_PLUS_2_YEARS = 2 * 24 * 60 * 60 * 1461 / 4;
- static const uint32_t EPOCH_PLUS_MINUTE = 60;
+// State that a LogBuffer may want to persist across calls to FlushTo().
+class FlushToState {
+ public:
+ FlushToState(uint64_t start, LogMask log_mask) : start_(start), log_mask_(log_mask) {}
+ virtual ~FlushToState() {}
- if (mono.tv_sec >= EPOCH_PLUS_2_YEARS) {
- return false;
- }
+ uint64_t start() const { return start_; }
+ void set_start(uint64_t start) { start_ = start; }
- log_time now(CLOCK_REALTIME);
+ LogMask log_mask() const { return log_mask_; }
- /* Timezone and ntp time setup? */
- if (now.tv_sec >= EPOCH_PLUS_2_YEARS) {
- return true;
- }
-
- /* no way to differentiate realtime from monotonic time */
- if (now.tv_sec < EPOCH_PLUS_MINUTE) {
- return false;
- }
-
- log_time cpu(CLOCK_MONOTONIC);
- /* too close to call to differentiate monotonic times from realtime */
- if ((cpu.tv_sec + EPOCH_PLUS_MINUTE) >= now.tv_sec) {
- return false;
- }
-
- /* dividing line half way between monotonic and realtime */
- return mono.tv_sec < ((cpu.tv_sec + now.tv_sec) / 2);
-}
-}
-
-typedef std::list<LogBufferElement*> LogBufferElementCollection;
-
-class LogBuffer {
- LogBufferElementCollection mLogElements;
- pthread_rwlock_t mLogElementsLock;
-
- LogStatistics stats;
-
- PruneList mPrune;
- // watermark for last per log id
- LogBufferElementCollection::iterator mLast[LOG_ID_MAX];
- bool mLastSet[LOG_ID_MAX];
- // watermark of any worst/chatty uid processing
- typedef std::unordered_map<uid_t, LogBufferElementCollection::iterator>
- LogBufferIteratorMap;
- LogBufferIteratorMap mLastWorst[LOG_ID_MAX];
- // watermark of any worst/chatty pid of system processing
- typedef std::unordered_map<pid_t, LogBufferElementCollection::iterator>
- LogBufferPidIteratorMap;
- LogBufferPidIteratorMap mLastWorstPidOfSystem[LOG_ID_MAX];
-
- unsigned long mMaxSize[LOG_ID_MAX];
-
- bool monotonic;
-
- LogTags tags;
-
- LogBufferElement* lastLoggedElements[LOG_ID_MAX];
- LogBufferElement* droppedElements[LOG_ID_MAX];
- void log(LogBufferElement* elem);
-
- public:
- LastLogTimes& mTimes;
-
- explicit LogBuffer(LastLogTimes* times);
- ~LogBuffer();
- void init();
- bool isMonotonic() {
- return monotonic;
- }
-
- int log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid, const char* msg,
- uint16_t len);
- // lastTid is an optional context to help detect if the last previous
- // valid message was from the same source so we can differentiate chatty
- // filter types (identical or expired)
- log_time flushTo(SocketClient* writer, const log_time& start,
- pid_t* lastTid, // &lastTid[LOG_ID_MAX] or nullptr
- bool privileged, bool security,
- int (*filter)(const LogBufferElement* element,
- void* arg) = nullptr,
- void* arg = nullptr);
-
- bool clear(log_id_t id, uid_t uid = AID_ROOT);
- unsigned long getSize(log_id_t id);
- int setSize(log_id_t id, unsigned long size);
- unsigned long getSizeUsed(log_id_t id);
-
- std::string formatStatistics(uid_t uid, pid_t pid, unsigned int logMask);
-
- void enableStatistics() {
- stats.enableStatistics();
- }
-
- int initPrune(const char* cp) {
- return mPrune.init(cp);
- }
- std::string formatPrune() {
- return mPrune.format();
- }
-
- std::string formatGetEventTag(uid_t uid, const char* name,
- const char* format) {
- return tags.formatGetEventTag(uid, name, format);
- }
- std::string formatEntry(uint32_t tag, uid_t uid) {
- return tags.formatEntry(tag, uid);
- }
- const char* tagToName(uint32_t tag) {
- return tags.tagToName(tag);
- }
-
- // helper must be protected directly or implicitly by wrlock()/unlock()
- const char* pidToName(pid_t pid) {
- return stats.pidToName(pid);
- }
- uid_t pidToUid(pid_t pid) { return stats.pidToUid(pid); }
- const char* uidToName(uid_t uid) {
- return stats.uidToName(uid);
- }
- void wrlock() {
- pthread_rwlock_wrlock(&mLogElementsLock);
- }
- void rdlock() {
- pthread_rwlock_rdlock(&mLogElementsLock);
- }
- void unlock() {
- pthread_rwlock_unlock(&mLogElementsLock);
- }
-
- private:
- static constexpr size_t minPrune = 4;
- static constexpr size_t maxPrune = 256;
- static const log_time pruneMargin;
-
- void maybePrune(log_id_t id);
- bool isBusy(log_time watermark);
- void kickMe(LogTimeEntry* me, log_id_t id, unsigned long pruneRows);
-
- bool prune(log_id_t id, unsigned long pruneRows, uid_t uid = AID_ROOT);
- LogBufferElementCollection::iterator erase(
- LogBufferElementCollection::iterator it, bool coalesce = false);
+ private:
+ uint64_t start_;
+ LogMask log_mask_;
};
-#endif // _LOGD_LOG_BUFFER_H__
+// Enum for the return values of the `filter` function passed to FlushTo().
+enum class FilterResult {
+ kSkip,
+ kStop,
+ kWrite,
+};
+
+class LogBuffer {
+ public:
+ virtual ~LogBuffer() {}
+
+ virtual void Init() = 0;
+
+ virtual int Log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid,
+ const char* msg, uint16_t len) = 0;
+
+ virtual std::unique_ptr<FlushToState> CreateFlushToState(uint64_t start, LogMask log_mask) = 0;
+ virtual bool FlushTo(
+ LogWriter* writer, FlushToState& state,
+ const std::function<FilterResult(log_id_t log_id, pid_t pid, uint64_t sequence,
+ log_time realtime)>& filter) = 0;
+
+ virtual bool Clear(log_id_t id, uid_t uid) = 0;
+ virtual unsigned long GetSize(log_id_t id) = 0;
+ virtual int SetSize(log_id_t id, unsigned long size) = 0;
+
+ virtual uint64_t sequence() const = 0;
+};
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index ec81933..ef9f1cf 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "LogBufferElement.h"
+
#include <ctype.h>
#include <endian.h>
#include <fcntl.h>
@@ -22,87 +24,109 @@
#include <time.h>
#include <unistd.h>
+#include <log/log_read.h>
#include <private/android_logger.h>
-#include "LogBuffer.h"
-#include "LogBufferElement.h"
-#include "LogCommand.h"
-#include "LogReader.h"
+#include "LogStatistics.h"
#include "LogUtils.h"
-const log_time LogBufferElement::FLUSH_ERROR((uint32_t)-1, (uint32_t)-1);
-atomic_int_fast64_t LogBufferElement::sequence(1);
-
-LogBufferElement::LogBufferElement(log_id_t log_id, log_time realtime,
- uid_t uid, pid_t pid, pid_t tid,
- const char* msg, uint16_t len)
- : mUid(uid),
- mPid(pid),
- mTid(tid),
- mRealTime(realtime),
- mMsgLen(len),
- mLogId(log_id),
- mDropped(false) {
- mMsg = new char[len];
- memcpy(mMsg, msg, len);
+LogBufferElement::LogBufferElement(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid,
+ pid_t tid, uint64_t sequence, const char* msg, uint16_t len)
+ : uid_(uid),
+ pid_(pid),
+ tid_(tid),
+ sequence_(sequence),
+ realtime_(realtime),
+ msg_len_(len),
+ log_id_(log_id),
+ dropped_(false) {
+ msg_ = new char[len];
+ memcpy(msg_, msg, len);
}
LogBufferElement::LogBufferElement(const LogBufferElement& elem)
- : mUid(elem.mUid),
- mPid(elem.mPid),
- mTid(elem.mTid),
- mRealTime(elem.mRealTime),
- mMsgLen(elem.mMsgLen),
- mLogId(elem.mLogId),
- mDropped(elem.mDropped) {
- if (mDropped) {
- mTag = elem.getTag();
+ : uid_(elem.uid_),
+ pid_(elem.pid_),
+ tid_(elem.tid_),
+ sequence_(elem.sequence_),
+ realtime_(elem.realtime_),
+ msg_len_(elem.msg_len_),
+ log_id_(elem.log_id_),
+ dropped_(elem.dropped_) {
+ if (dropped_) {
+ tag_ = elem.GetTag();
} else {
- mMsg = new char[mMsgLen];
- memcpy(mMsg, elem.mMsg, mMsgLen);
+ msg_ = new char[msg_len_];
+ memcpy(msg_, elem.msg_, msg_len_);
+ }
+}
+
+LogBufferElement::LogBufferElement(LogBufferElement&& elem)
+ : uid_(elem.uid_),
+ pid_(elem.pid_),
+ tid_(elem.tid_),
+ sequence_(elem.sequence_),
+ realtime_(elem.realtime_),
+ msg_len_(elem.msg_len_),
+ log_id_(elem.log_id_),
+ dropped_(elem.dropped_) {
+ if (dropped_) {
+ tag_ = elem.GetTag();
+ } else {
+ msg_ = elem.msg_;
+ elem.msg_ = nullptr;
}
}
LogBufferElement::~LogBufferElement() {
- if (!mDropped) {
- delete[] mMsg;
+ if (!dropped_) {
+ delete[] msg_;
}
}
-uint32_t LogBufferElement::getTag() const {
+uint32_t LogBufferElement::GetTag() const {
// Binary buffers have no tag.
- if (!isBinary()) {
+ if (!IsBinary(log_id())) {
return 0;
}
- // Dropped messages store the tag in place of mMsg.
- if (mDropped) {
- return mTag;
+ // Dropped messages store the tag in place of msg_.
+ if (dropped_) {
+ return tag_;
}
- // For non-dropped messages, we get the tag from the message header itself.
- if (mMsgLen < sizeof(android_event_header_t)) {
- return 0;
- }
-
- return reinterpret_cast<const android_event_header_t*>(mMsg)->tag;
+ return MsgToTag(msg(), msg_len());
}
-uint16_t LogBufferElement::setDropped(uint16_t value) {
- if (mDropped) {
- return mDroppedCount = value;
+LogStatisticsElement LogBufferElement::ToLogStatisticsElement() const {
+ return LogStatisticsElement{
+ .uid = uid(),
+ .pid = pid(),
+ .tid = tid(),
+ .tag = GetTag(),
+ .realtime = realtime(),
+ .msg = msg(),
+ .msg_len = msg_len(),
+ .dropped_count = dropped_count(),
+ .log_id = log_id(),
+ };
+}
+
+uint16_t LogBufferElement::SetDropped(uint16_t value) {
+ if (dropped_) {
+ return dropped_count_ = value;
}
- // The tag information is saved in mMsg data, which is in a union with mTag, used after mDropped
- // is set to true. Therefore we save the tag value aside, delete mMsg, then set mTag to the tag
+ // The tag information is saved in msg_ data, which is in a union with tag_, used after dropped_
+ // is set to true. Therefore we save the tag value aside, delete msg_, then set tag_ to the tag
// value in its place.
- auto old_tag = getTag();
- delete[] mMsg;
- mMsg = nullptr;
+ auto old_tag = GetTag();
+ delete[] msg_;
+ msg_ = nullptr;
- mTag = old_tag;
- mDropped = true;
- return mDroppedCount = value;
+ tag_ = old_tag;
+ dropped_ = true;
+ return dropped_count_ = value;
}
// caller must own and free character string
@@ -150,8 +174,8 @@
return retval;
}
-// assumption: mMsg == NULL
-size_t LogBufferElement::populateDroppedMessage(char*& buffer, LogBuffer* parent,
+// assumption: msg_ == NULL
+size_t LogBufferElement::PopulateDroppedMessage(char*& buffer, LogStatistics* stats,
bool lastSame) {
static const char tag[] = "chatty";
@@ -161,17 +185,13 @@
}
static const char format_uid[] = "uid=%u%s%s %s %u line%s";
- parent->wrlock();
- const char* name = parent->uidToName(mUid);
- parent->unlock();
- const char* commName = android::tidToName(mTid);
- if (!commName && (mTid != mPid)) {
- commName = android::tidToName(mPid);
+ const char* name = stats->UidToName(uid_);
+ const char* commName = android::tidToName(tid_);
+ if (!commName && (tid_ != pid_)) {
+ commName = android::tidToName(pid_);
}
if (!commName) {
- parent->wrlock();
- commName = parent->pidToName(mPid);
- parent->unlock();
+ commName = stats->PidToName(pid_);
}
if (name && name[0] && commName && (name[0] == commName[0])) {
size_t len = strlen(name + 1);
@@ -187,28 +207,27 @@
}
if (name) {
char* buf = nullptr;
- asprintf(&buf, "(%s)", name);
- if (buf) {
+ int result = asprintf(&buf, "(%s)", name);
+ if (result != -1) {
free(const_cast<char*>(name));
name = buf;
}
}
if (commName) {
char* buf = nullptr;
- asprintf(&buf, " %s", commName);
- if (buf) {
+ int result = asprintf(&buf, " %s", commName);
+ if (result != -1) {
free(const_cast<char*>(commName));
commName = buf;
}
}
// identical to below to calculate the buffer size required
const char* type = lastSame ? "identical" : "expire";
- size_t len = snprintf(nullptr, 0, format_uid, mUid, name ? name : "",
- commName ? commName : "", type, getDropped(),
- (getDropped() > 1) ? "s" : "");
+ size_t len = snprintf(nullptr, 0, format_uid, uid_, name ? name : "", commName ? commName : "",
+ type, dropped_count(), (dropped_count() > 1) ? "s" : "");
size_t hdrLen;
- if (isBinary()) {
+ if (IsBinary(log_id())) {
hdrLen = sizeof(android_log_event_string_t);
} else {
hdrLen = 1 + sizeof(tag);
@@ -222,7 +241,7 @@
}
size_t retval = hdrLen + len;
- if (isBinary()) {
+ if (IsBinary(log_id())) {
android_log_event_string_t* event =
reinterpret_cast<android_log_event_string_t*>(buffer);
@@ -235,45 +254,37 @@
strcpy(buffer + 1, tag);
}
- snprintf(buffer + hdrLen, len + 1, format_uid, mUid, name ? name : "",
- commName ? commName : "", type, getDropped(),
- (getDropped() > 1) ? "s" : "");
+ snprintf(buffer + hdrLen, len + 1, format_uid, uid_, name ? name : "", commName ? commName : "",
+ type, dropped_count(), (dropped_count() > 1) ? "s" : "");
free(const_cast<char*>(name));
free(const_cast<char*>(commName));
return retval;
}
-log_time LogBufferElement::flushTo(SocketClient* reader, LogBuffer* parent, bool lastSame) {
+bool LogBufferElement::FlushTo(LogWriter* writer, LogStatistics* stats, bool lastSame) {
struct logger_entry entry = {};
entry.hdr_size = sizeof(struct logger_entry);
- entry.lid = mLogId;
- entry.pid = mPid;
- entry.tid = mTid;
- entry.uid = mUid;
- entry.sec = mRealTime.tv_sec;
- entry.nsec = mRealTime.tv_nsec;
-
- struct iovec iovec[2];
- iovec[0].iov_base = &entry;
- iovec[0].iov_len = entry.hdr_size;
+ entry.lid = log_id_;
+ entry.pid = pid_;
+ entry.tid = tid_;
+ entry.uid = uid_;
+ entry.sec = realtime_.tv_sec;
+ entry.nsec = realtime_.tv_nsec;
char* buffer = nullptr;
-
- if (mDropped) {
- entry.len = populateDroppedMessage(buffer, parent, lastSame);
- if (!entry.len) return mRealTime;
- iovec[1].iov_base = buffer;
+ const char* msg;
+ if (dropped_) {
+ entry.len = PopulateDroppedMessage(buffer, stats, lastSame);
+ if (!entry.len) return true;
+ msg = buffer;
} else {
- entry.len = mMsgLen;
- iovec[1].iov_base = mMsg;
+ msg = msg_;
+ entry.len = msg_len_;
}
- iovec[1].iov_len = entry.len;
- log_time retval = reader->sendDatav(iovec, 1 + (entry.len != 0))
- ? FLUSH_ERROR
- : mRealTime;
+ bool retval = writer->Write(entry, msg);
if (buffer) free(buffer);
diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h
index fd790e4..fd5d88f 100644
--- a/logd/LogBufferElement.h
+++ b/logd/LogBufferElement.h
@@ -16,15 +16,15 @@
#pragma once
-#include <stdatomic.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/types.h>
#include <log/log.h>
-#include <sysutils/SocketClient.h>
-class LogBuffer;
+#include "LogWriter.h"
+
+#include "LogStatistics.h"
#define EXPIRE_HOUR_THRESHOLD 24 // Only expire chatty UID logs to preserve
// non-chatty UIDs less than this age in hours
@@ -33,67 +33,48 @@
#define EXPIRE_RATELIMIT 10 // maximum rate in seconds to report expiration
class __attribute__((packed)) LogBufferElement {
- friend LogBuffer;
-
- // sized to match reality of incoming log packets
- const uint32_t mUid;
- const uint32_t mPid;
- const uint32_t mTid;
- log_time mRealTime;
- union {
- char* mMsg; // mDropped == false
- int32_t mTag; // mDropped == true
- };
- union {
- const uint16_t mMsgLen; // mDropped == false
- uint16_t mDroppedCount; // mDropped == true
- };
- const uint8_t mLogId;
- bool mDropped;
-
- static atomic_int_fast64_t sequence;
-
- // assumption: mDropped == true
- size_t populateDroppedMessage(char*& buffer, LogBuffer* parent,
- bool lastSame);
-
- public:
- LogBufferElement(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid,
- pid_t tid, const char* msg, uint16_t len);
+ public:
+ LogBufferElement(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid,
+ uint64_t sequence, const char* msg, uint16_t len);
LogBufferElement(const LogBufferElement& elem);
+ LogBufferElement(LogBufferElement&& elem);
~LogBufferElement();
- bool isBinary(void) const {
- return (mLogId == LOG_ID_EVENTS) || (mLogId == LOG_ID_SECURITY);
- }
+ uint32_t GetTag() const;
+ uint16_t SetDropped(uint16_t value);
- log_id_t getLogId() const {
- return static_cast<log_id_t>(mLogId);
- }
- uid_t getUid(void) const {
- return mUid;
- }
- pid_t getPid(void) const {
- return mPid;
- }
- pid_t getTid(void) const {
- return mTid;
- }
- uint32_t getTag() const;
- uint16_t getDropped(void) const {
- return mDropped ? mDroppedCount : 0;
- }
- uint16_t setDropped(uint16_t value);
- uint16_t getMsgLen() const {
- return mDropped ? 0 : mMsgLen;
- }
- const char* getMsg() const {
- return mDropped ? nullptr : mMsg;
- }
- log_time getRealTime(void) const {
- return mRealTime;
- }
+ bool FlushTo(LogWriter* writer, LogStatistics* parent, bool lastSame);
- static const log_time FLUSH_ERROR;
- log_time flushTo(SocketClient* writer, LogBuffer* parent, bool lastSame);
+ LogStatisticsElement ToLogStatisticsElement() const;
+
+ log_id_t log_id() const { return static_cast<log_id_t>(log_id_); }
+ uid_t uid() const { return uid_; }
+ pid_t pid() const { return pid_; }
+ pid_t tid() const { return tid_; }
+ uint16_t msg_len() const { return dropped_ ? 0 : msg_len_; }
+ const char* msg() const { return dropped_ ? nullptr : msg_; }
+ uint64_t sequence() const { return sequence_; }
+ log_time realtime() const { return realtime_; }
+ uint16_t dropped_count() const { return dropped_ ? dropped_count_ : 0; }
+
+ private:
+ // assumption: mDropped == true
+ size_t PopulateDroppedMessage(char*& buffer, LogStatistics* parent, bool lastSame);
+
+ // sized to match reality of incoming log packets
+ const uint32_t uid_;
+ const uint32_t pid_;
+ const uint32_t tid_;
+ uint64_t sequence_;
+ log_time realtime_;
+ union {
+ char* msg_; // mDropped == false
+ int32_t tag_; // mDropped == true
+ };
+ union {
+ const uint16_t msg_len_; // mDropped == false
+ uint16_t dropped_count_; // mDropped == true
+ };
+ const uint8_t log_id_;
+ bool dropped_;
};
diff --git a/logd/LogBufferTest.cpp b/logd/LogBufferTest.cpp
new file mode 100644
index 0000000..e651b4f
--- /dev/null
+++ b/logd/LogBufferTest.cpp
@@ -0,0 +1,366 @@
+/*
+ * 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 "LogBufferTest.h"
+
+#include <unistd.h>
+
+#include <limits>
+#include <memory>
+#include <regex>
+#include <vector>
+
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include "LogReaderThread.h"
+#include "LogWriter.h"
+
+using android::base::Join;
+using android::base::Split;
+using android::base::StringPrintf;
+
+#ifndef __ANDROID__
+unsigned long __android_logger_get_buffer_size(log_id_t) {
+ return 1024 * 1024;
+}
+
+bool __android_logger_valid_buffer_size(unsigned long) {
+ return true;
+}
+#endif
+
+char* android::uidToName(uid_t) {
+ return nullptr;
+}
+
+static std::vector<std::string> CompareLoggerEntries(const logger_entry& expected,
+ const logger_entry& result, bool ignore_len) {
+ std::vector<std::string> errors;
+ if (!ignore_len && expected.len != result.len) {
+ errors.emplace_back(
+ StringPrintf("len: expected %" PRIu16 " vs %" PRIu16, expected.len, result.len));
+ }
+ if (expected.hdr_size != result.hdr_size) {
+ errors.emplace_back(StringPrintf("hdr_size: %" PRIu16 " vs %" PRIu16, expected.hdr_size,
+ result.hdr_size));
+ }
+ if (expected.pid != result.pid) {
+ errors.emplace_back(
+ StringPrintf("pid: expected %" PRIi32 " vs %" PRIi32, expected.pid, result.pid));
+ }
+ if (expected.tid != result.tid) {
+ errors.emplace_back(
+ StringPrintf("tid: expected %" PRIu32 " vs %" PRIu32, expected.tid, result.tid));
+ }
+ if (expected.sec != result.sec) {
+ errors.emplace_back(
+ StringPrintf("sec: expected %" PRIu32 " vs %" PRIu32, expected.sec, result.sec));
+ }
+ if (expected.nsec != result.nsec) {
+ errors.emplace_back(
+ StringPrintf("nsec: expected %" PRIu32 " vs %" PRIu32, expected.nsec, result.nsec));
+ }
+ if (expected.lid != result.lid) {
+ errors.emplace_back(
+ StringPrintf("lid: expected %" PRIu32 " vs %" PRIu32, expected.lid, result.lid));
+ }
+ if (expected.uid != result.uid) {
+ errors.emplace_back(
+ StringPrintf("uid: expected %" PRIu32 " vs %" PRIu32, expected.uid, result.uid));
+ }
+ return errors;
+}
+
+static std::string MakePrintable(std::string in) {
+ if (in.size() > 80) {
+ in = in.substr(0, 80) + "...";
+ }
+ std::string result;
+ for (const char c : in) {
+ if (isprint(c)) {
+ result.push_back(c);
+ } else {
+ result.append(StringPrintf("\\%02x", static_cast<int>(c) & 0xFF));
+ }
+ }
+ return result;
+}
+
+static std::string CompareMessages(const std::string& expected, const std::string& result) {
+ if (expected == result) {
+ return {};
+ }
+ size_t diff_index = 0;
+ for (; diff_index < std::min(expected.size(), result.size()); ++diff_index) {
+ if (expected[diff_index] != result[diff_index]) {
+ break;
+ }
+ }
+
+ if (diff_index < 80) {
+ auto expected_short = MakePrintable(expected);
+ auto result_short = MakePrintable(result);
+ return StringPrintf("msg: expected '%s' vs '%s'", expected_short.c_str(),
+ result_short.c_str());
+ }
+
+ auto expected_short = MakePrintable(expected.substr(diff_index));
+ auto result_short = MakePrintable(result.substr(diff_index));
+ return StringPrintf("msg: index %zu: expected '%s' vs '%s'", diff_index, expected_short.c_str(),
+ result_short.c_str());
+}
+
+static std::string CompareRegexMessages(const std::string& expected, const std::string& result) {
+ auto expected_pieces = Split(expected, std::string("\0", 1));
+ auto result_pieces = Split(result, std::string("\0", 1));
+
+ if (expected_pieces.size() != 3 || result_pieces.size() != 3) {
+ return StringPrintf(
+ "msg: should have 3 null delimited strings found %d in expected, %d in result: "
+ "'%s' vs '%s'",
+ static_cast<int>(expected_pieces.size()), static_cast<int>(result_pieces.size()),
+ MakePrintable(expected).c_str(), MakePrintable(result).c_str());
+ }
+ if (expected_pieces[0] != result_pieces[0]) {
+ return StringPrintf("msg: tag/priority mismatch expected '%s' vs '%s'",
+ MakePrintable(expected_pieces[0]).c_str(),
+ MakePrintable(result_pieces[0]).c_str());
+ }
+ std::regex expected_tag_regex(expected_pieces[1]);
+ if (!std::regex_search(result_pieces[1], expected_tag_regex)) {
+ return StringPrintf("msg: message regex mismatch expected '%s' vs '%s'",
+ MakePrintable(expected_pieces[1]).c_str(),
+ MakePrintable(result_pieces[1]).c_str());
+ }
+ if (expected_pieces[2] != result_pieces[2]) {
+ return StringPrintf("msg: nothing expected after final null character '%s' vs '%s'",
+ MakePrintable(expected_pieces[2]).c_str(),
+ MakePrintable(result_pieces[2]).c_str());
+ }
+ return {};
+}
+
+void CompareLogMessages(const std::vector<LogMessage>& expected,
+ const std::vector<LogMessage>& result) {
+ EXPECT_EQ(expected.size(), result.size());
+ size_t end = std::min(expected.size(), result.size());
+ size_t num_errors = 0;
+ for (size_t i = 0; i < end; ++i) {
+ auto errors =
+ CompareLoggerEntries(expected[i].entry, result[i].entry, expected[i].regex_compare);
+ auto msg_error = expected[i].regex_compare
+ ? CompareRegexMessages(expected[i].message, result[i].message)
+ : CompareMessages(expected[i].message, result[i].message);
+ if (!msg_error.empty()) {
+ errors.emplace_back(msg_error);
+ }
+ if (!errors.empty()) {
+ GTEST_LOG_(ERROR) << "Mismatch log message " << i << "\n" << Join(errors, "\n");
+ ++num_errors;
+ }
+ }
+ EXPECT_EQ(0U, num_errors);
+}
+
+void FixupMessages(std::vector<LogMessage>* messages) {
+ for (auto& [entry, message, _] : *messages) {
+ entry.hdr_size = sizeof(logger_entry);
+ entry.len = message.size();
+ }
+}
+
+TEST_P(LogBufferTest, smoke) {
+ std::vector<LogMessage> log_messages = {
+ {{
+ .pid = 1,
+ .tid = 1,
+ .sec = 1234,
+ .nsec = 323001,
+ .lid = LOG_ID_MAIN,
+ .uid = 0,
+ },
+ "smoke test"},
+ };
+ FixupMessages(&log_messages);
+ LogMessages(log_messages);
+
+ std::vector<LogMessage> read_log_messages;
+ std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, nullptr));
+ std::unique_ptr<FlushToState> flush_to_state = log_buffer_->CreateFlushToState(1, kLogMaskAll);
+ EXPECT_TRUE(log_buffer_->FlushTo(test_writer.get(), *flush_to_state, nullptr));
+ EXPECT_EQ(2ULL, flush_to_state->start());
+ CompareLogMessages(log_messages, read_log_messages);
+}
+
+TEST_P(LogBufferTest, smoke_with_reader_thread) {
+ std::vector<LogMessage> log_messages = {
+ {{.pid = 1, .tid = 2, .sec = 10000, .nsec = 20001, .lid = LOG_ID_MAIN, .uid = 0},
+ "first"},
+ {{.pid = 10, .tid = 2, .sec = 10000, .nsec = 20002, .lid = LOG_ID_MAIN, .uid = 0},
+ "second"},
+ {{.pid = 100, .tid = 2, .sec = 10000, .nsec = 20003, .lid = LOG_ID_KERNEL, .uid = 0},
+ "third"},
+ {{.pid = 10, .tid = 2, .sec = 10000, .nsec = 20004, .lid = LOG_ID_MAIN, .uid = 0},
+ "fourth"},
+ {{.pid = 1, .tid = 2, .sec = 10000, .nsec = 20005, .lid = LOG_ID_RADIO, .uid = 0},
+ "fifth"},
+ {{.pid = 2, .tid = 2, .sec = 10000, .nsec = 20006, .lid = LOG_ID_RADIO, .uid = 0},
+ "sixth"},
+ {{.pid = 3, .tid = 2, .sec = 10000, .nsec = 20007, .lid = LOG_ID_RADIO, .uid = 0},
+ "seventh"},
+ {{.pid = 4, .tid = 2, .sec = 10000, .nsec = 20008, .lid = LOG_ID_MAIN, .uid = 0},
+ "eighth"},
+ {{.pid = 5, .tid = 2, .sec = 10000, .nsec = 20009, .lid = LOG_ID_CRASH, .uid = 0},
+ "nineth"},
+ {{.pid = 6, .tid = 2, .sec = 10000, .nsec = 20011, .lid = LOG_ID_MAIN, .uid = 0},
+ "tenth"},
+ };
+ FixupMessages(&log_messages);
+ LogMessages(log_messages);
+
+ std::vector<LogMessage> read_log_messages;
+ bool released = false;
+
+ {
+ auto lock = std::unique_lock{reader_list_.reader_threads_lock()};
+ std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, &released));
+ std::unique_ptr<LogReaderThread> log_reader(
+ new LogReaderThread(log_buffer_.get(), &reader_list_, std::move(test_writer), true,
+ 0, ~0, 0, {}, 1, {}));
+ reader_list_.reader_threads().emplace_back(std::move(log_reader));
+ }
+
+ while (!released) {
+ usleep(5000);
+ }
+ {
+ auto lock = std::unique_lock{reader_list_.reader_threads_lock()};
+ EXPECT_EQ(0U, reader_list_.reader_threads().size());
+ }
+ CompareLogMessages(log_messages, read_log_messages);
+}
+
+// Generate random messages, set the 'sec' parameter explicit though, to be able to track the
+// expected order of messages.
+LogMessage GenerateRandomLogMessage(uint32_t sec) {
+ auto rand_uint32 = [](int max) -> uint32_t { return rand() % max; };
+ logger_entry entry = {
+ .hdr_size = sizeof(logger_entry),
+ .pid = rand() % 5000,
+ .tid = rand_uint32(5000),
+ .sec = sec,
+ .nsec = rand_uint32(NS_PER_SEC),
+ .lid = rand_uint32(LOG_ID_STATS),
+ .uid = rand_uint32(100000),
+ };
+
+ // See comment in ChattyLogBuffer::Log() for why this is disallowed.
+ if (entry.nsec % 1000 == 0) {
+ ++entry.nsec;
+ }
+
+ if (entry.lid == LOG_ID_EVENTS) {
+ entry.lid = LOG_ID_KERNEL;
+ }
+
+ std::string message;
+ char priority = ANDROID_LOG_INFO + rand() % 2;
+ message.push_back(priority);
+
+ int tag_length = 2 + rand() % 10;
+ for (int i = 0; i < tag_length; ++i) {
+ message.push_back('a' + rand() % 26);
+ }
+ message.push_back('\0');
+
+ int msg_length = 2 + rand() % 1000;
+ for (int i = 0; i < msg_length; ++i) {
+ message.push_back('a' + rand() % 26);
+ }
+ message.push_back('\0');
+
+ entry.len = message.size();
+
+ return {entry, message};
+}
+
+TEST_P(LogBufferTest, random_messages) {
+ srand(1);
+ std::vector<LogMessage> log_messages;
+ for (size_t i = 0; i < 1000; ++i) {
+ log_messages.emplace_back(GenerateRandomLogMessage(i));
+ }
+ LogMessages(log_messages);
+
+ std::vector<LogMessage> read_log_messages;
+ bool released = false;
+
+ {
+ auto lock = std::unique_lock{reader_list_.reader_threads_lock()};
+ std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, &released));
+ std::unique_ptr<LogReaderThread> log_reader(
+ new LogReaderThread(log_buffer_.get(), &reader_list_, std::move(test_writer), true,
+ 0, ~0, 0, {}, 1, {}));
+ reader_list_.reader_threads().emplace_back(std::move(log_reader));
+ }
+
+ while (!released) {
+ usleep(5000);
+ }
+ {
+ auto lock = std::unique_lock{reader_list_.reader_threads_lock()};
+ EXPECT_EQ(0U, reader_list_.reader_threads().size());
+ }
+ CompareLogMessages(log_messages, read_log_messages);
+}
+
+TEST_P(LogBufferTest, read_last_sequence) {
+ std::vector<LogMessage> log_messages = {
+ {{.pid = 1, .tid = 2, .sec = 10000, .nsec = 20001, .lid = LOG_ID_MAIN, .uid = 0},
+ "first"},
+ {{.pid = 10, .tid = 2, .sec = 10000, .nsec = 20002, .lid = LOG_ID_MAIN, .uid = 0},
+ "second"},
+ {{.pid = 100, .tid = 2, .sec = 10000, .nsec = 20003, .lid = LOG_ID_MAIN, .uid = 0},
+ "third"},
+ };
+ FixupMessages(&log_messages);
+ LogMessages(log_messages);
+
+ std::vector<LogMessage> read_log_messages;
+ bool released = false;
+
+ {
+ auto lock = std::unique_lock{reader_list_.reader_threads_lock()};
+ std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, &released));
+ std::unique_ptr<LogReaderThread> log_reader(
+ new LogReaderThread(log_buffer_.get(), &reader_list_, std::move(test_writer), true,
+ 0, ~0, 0, {}, 3, {}));
+ reader_list_.reader_threads().emplace_back(std::move(log_reader));
+ }
+
+ while (!released) {
+ usleep(5000);
+ }
+ {
+ auto lock = std::unique_lock{reader_list_.reader_threads_lock()};
+ EXPECT_EQ(0U, reader_list_.reader_threads().size());
+ }
+ std::vector<LogMessage> expected_log_messages = {log_messages.back()};
+ CompareLogMessages(expected_log_messages, read_log_messages);
+}
+
+INSTANTIATE_TEST_CASE_P(LogBufferTests, LogBufferTest, testing::Values("chatty", "simple"));
diff --git a/logd/LogBufferTest.h b/logd/LogBufferTest.h
new file mode 100644
index 0000000..f91a1b5
--- /dev/null
+++ b/logd/LogBufferTest.h
@@ -0,0 +1,89 @@
+/*
+ * 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 <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "ChattyLogBuffer.h"
+#include "LogReaderList.h"
+#include "LogStatistics.h"
+#include "LogTags.h"
+#include "LogWhiteBlackList.h"
+#include "SimpleLogBuffer.h"
+
+struct LogMessage {
+ logger_entry entry;
+ std::string message;
+ bool regex_compare = false; // Only set for expected messages, when true 'message' should be
+ // interpretted as a regex.
+};
+
+// Compares the ordered list of expected and result, causing a test failure with appropriate
+// information on failure.
+void CompareLogMessages(const std::vector<LogMessage>& expected,
+ const std::vector<LogMessage>& result);
+// Sets hdr_size and len parameters appropriately.
+void FixupMessages(std::vector<LogMessage>* messages);
+
+class TestWriter : public LogWriter {
+ public:
+ TestWriter(std::vector<LogMessage>* msgs, bool* released)
+ : LogWriter(0, true), msgs_(msgs), released_(released) {}
+ bool Write(const logger_entry& entry, const char* message) override {
+ msgs_->emplace_back(LogMessage{entry, std::string(message, entry.len), false});
+ return true;
+ }
+
+ void Release() {
+ if (released_) *released_ = true;
+ }
+
+ std::string name() const override { return "test_writer"; }
+
+ private:
+ std::vector<LogMessage>* msgs_;
+ bool* released_;
+};
+
+class LogBufferTest : public testing::TestWithParam<std::string> {
+ protected:
+ void SetUp() override {
+ if (GetParam() == "chatty") {
+ log_buffer_.reset(new ChattyLogBuffer(&reader_list_, &tags_, &prune_, &stats_));
+ } else if (GetParam() == "simple") {
+ log_buffer_.reset(new SimpleLogBuffer(&reader_list_, &tags_, &stats_));
+ } else {
+ FAIL() << "Unknown buffer type selected for test";
+ }
+ }
+
+ void LogMessages(const std::vector<LogMessage>& messages) {
+ for (auto& [entry, message, _] : messages) {
+ log_buffer_->Log(static_cast<log_id_t>(entry.lid), log_time(entry.sec, entry.nsec),
+ entry.uid, entry.pid, entry.tid, message.c_str(), message.size());
+ }
+ }
+
+ LogReaderList reader_list_;
+ LogTags tags_;
+ PruneList prune_;
+ LogStatistics stats_{false};
+ std::unique_ptr<LogBuffer> log_buffer_;
+};
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index edd326a..1ea87a9 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "LogKlog.h"
+
#include <ctype.h>
#include <errno.h>
#include <inttypes.h>
@@ -29,8 +31,6 @@
#include <private/android_logger.h>
#include "LogBuffer.h"
-#include "LogKlog.h"
-#include "LogReader.h"
#define KMSG_PRIORITY(PRI) \
'<', '0' + (LOG_SYSLOG | (PRI)) / 10, '0' + (LOG_SYSLOG | (PRI)) % 10, '>'
@@ -201,15 +201,14 @@
? log_time(log_time::EPOCH)
: (log_time(CLOCK_REALTIME) - log_time(CLOCK_MONOTONIC));
-LogKlog::LogKlog(LogBuffer* buf, LogReader* reader, int fdWrite, int fdRead,
- bool auditd)
+LogKlog::LogKlog(LogBuffer* buf, int fdWrite, int fdRead, bool auditd, LogStatistics* stats)
: SocketListener(fdRead, false),
logbuf(buf),
- reader(reader),
signature(CLOCK_MONOTONIC),
initialized(false),
enableLogging(true),
- auditd(auditd) {
+ auditd(auditd),
+ stats_(stats) {
static const char klogd_message[] = "%s%s%" PRIu64 "\n";
char buffer[strlen(priority_message) + strlen(klogdStr) +
strlen(klogd_message) + 20];
@@ -309,8 +308,6 @@
}
buf = cp;
- if (isMonotonic()) return now;
-
const char* b;
if (((b = android::strnstr(cp, len, suspendStr))) &&
(((b += strlen(suspendStr)) - cp) < len)) {
@@ -356,11 +353,7 @@
convertMonotonicToReal(now);
} else {
- if (isMonotonic()) {
- now = log_time(CLOCK_MONOTONIC);
- } else {
- now = log_time(CLOCK_REALTIME);
- }
+ now = log_time(CLOCK_REALTIME);
}
return now;
}
@@ -431,45 +424,6 @@
return pri;
}
-// Passed the entire SYSLOG_ACTION_READ_ALL buffer and interpret a
-// compensated start time.
-void LogKlog::synchronize(const char* buf, ssize_t len) {
- const char* cp = android::strnstr(buf, len, suspendStr);
- if (!cp) {
- cp = android::strnstr(buf, len, resumeStr);
- if (!cp) return;
- } else {
- const char* rp = android::strnstr(buf, len, resumeStr);
- if (rp && (rp < cp)) cp = rp;
- }
-
- do {
- --cp;
- } while ((cp > buf) && (*cp != '\n'));
- if (*cp == '\n') {
- ++cp;
- }
- parseKernelPrio(cp, len - (cp - buf));
-
- log_time now = sniffTime(cp, len - (cp - buf), true);
-
- const char* suspended = android::strnstr(buf, len, suspendedStr);
- if (!suspended || (suspended > cp)) {
- return;
- }
- cp = suspended;
-
- do {
- --cp;
- } while ((cp > buf) && (*cp != '\n'));
- if (*cp == '\n') {
- ++cp;
- }
- parseKernelPrio(cp, len - (cp - buf));
-
- sniffTime(cp, len - (cp - buf), true);
-}
-
// Convert kernel log priority number into an Android Logger priority number
static int convertKernelPrioToAndroidPrio(int pri) {
switch (pri & LOG_PRIMASK) {
@@ -573,9 +527,7 @@
const pid_t tid = pid;
uid_t uid = AID_ROOT;
if (pid) {
- logbuf->wrlock();
- uid = logbuf->pidToUid(pid);
- logbuf->unlock();
+ uid = stats_->PidToUid(pid);
}
// Parse (rules at top) to pull out a tag from the incoming kernel message.
@@ -789,7 +741,7 @@
memcpy(np, p, b);
np[b] = '\0';
- if (!isMonotonic()) {
+ {
// Watch out for singular race conditions with timezone causing near
// integer quarter-hour jumps in the time and compensate accordingly.
// Entries will be temporal within near_seconds * 2. b/21868540
@@ -815,12 +767,7 @@
}
// Log message
- int rc = logbuf->log(LOG_ID_KERNEL, now, uid, pid, tid, newstr, (uint16_t)n);
-
- // notify readers
- if (rc > 0) {
- reader->notifyNewLog(static_cast<log_mask_t>(1 << LOG_ID_KERNEL));
- }
+ int rc = logbuf->Log(LOG_ID_KERNEL, now, uid, pid, tid, newstr, (uint16_t)n);
return rc;
}
diff --git a/logd/LogKlog.h b/logd/LogKlog.h
index 6bfd6a8..56e0452 100644
--- a/logd/LogKlog.h
+++ b/logd/LogKlog.h
@@ -14,18 +14,16 @@
* limitations under the License.
*/
-#ifndef _LOGD_LOG_KLOG_H__
-#define _LOGD_LOG_KLOG_H__
+#pragma once
#include <private/android_logger.h>
#include <sysutils/SocketListener.h>
-class LogBuffer;
-class LogReader;
+#include "LogBuffer.h"
+#include "LogStatistics.h"
class LogKlog : public SocketListener {
LogBuffer* logbuf;
- LogReader* reader;
const log_time signature;
// Set once thread is started, separates KLOG_ACTION_READ_ALL
// and KLOG_ACTION_READ phases.
@@ -38,27 +36,18 @@
static log_time correction;
- public:
- LogKlog(LogBuffer* buf, LogReader* reader, int fdWrite, int fdRead,
- bool auditd);
+ public:
+ LogKlog(LogBuffer* buf, int fdWrite, int fdRead, bool auditd, LogStatistics* stats);
int log(const char* buf, ssize_t len);
- void synchronize(const char* buf, ssize_t len);
- bool isMonotonic() {
- return logbuf->isMonotonic();
- }
- static void convertMonotonicToReal(log_time& real) {
- real += correction;
- }
- static void convertRealToMonotonic(log_time& real) {
- real -= correction;
- }
+ static void convertMonotonicToReal(log_time& real) { real += correction; }
- protected:
- log_time sniffTime(const char*& buf, ssize_t len, bool reverse);
- pid_t sniffPid(const char*& buf, ssize_t len);
- void calculateCorrection(const log_time& monotonic, const char* real_string, ssize_t len);
- virtual bool onDataAvailable(SocketClient* cli);
+ protected:
+ log_time sniffTime(const char*& buf, ssize_t len, bool reverse);
+ pid_t sniffPid(const char*& buf, ssize_t len);
+ void calculateCorrection(const log_time& monotonic, const char* real_string, ssize_t len);
+ virtual bool onDataAvailable(SocketClient* cli);
+
+ private:
+ LogStatistics* stats_;
};
-
-#endif
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp
index ba61042..a6ab50b 100644
--- a/logd/LogListener.cpp
+++ b/logd/LogListener.cpp
@@ -22,42 +22,53 @@
#include <sys/un.h>
#include <unistd.h>
+#include <thread>
+
#include <cutils/sockets.h>
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
#include "LogBuffer.h"
#include "LogListener.h"
-#include "LogUtils.h"
+#include "LogPermissions.h"
-LogListener::LogListener(LogBuffer* buf, LogReader* reader)
- : SocketListener(getLogSocket(), false), logbuf(buf), reader(reader) {}
+LogListener::LogListener(LogBuffer* buf) : socket_(GetLogSocket()), logbuf_(buf) {}
-bool LogListener::onDataAvailable(SocketClient* cli) {
- static bool name_set;
- if (!name_set) {
- prctl(PR_SET_NAME, "logd.writer");
- name_set = true;
+bool LogListener::StartListener() {
+ if (socket_ <= 0) {
+ return false;
}
+ auto thread = std::thread(&LogListener::ThreadFunction, this);
+ thread.detach();
+ return true;
+}
+void LogListener::ThreadFunction() {
+ prctl(PR_SET_NAME, "logd.writer");
+
+ while (true) {
+ HandleData();
+ }
+}
+
+void LogListener::HandleData() {
// + 1 to ensure null terminator if MAX_PAYLOAD buffer is received
- char buffer[sizeof(android_log_header_t) + LOGGER_ENTRY_MAX_PAYLOAD + 1];
- struct iovec iov = { buffer, sizeof(buffer) - 1 };
+ __attribute__((uninitialized)) char
+ buffer[sizeof(android_log_header_t) + LOGGER_ENTRY_MAX_PAYLOAD + 1];
+ struct iovec iov = {buffer, sizeof(buffer) - 1};
alignas(4) char control[CMSG_SPACE(sizeof(struct ucred))];
struct msghdr hdr = {
nullptr, 0, &iov, 1, control, sizeof(control), 0,
};
- int socket = cli->getSocket();
-
// To clear the entire buffer is secure/safe, but this contributes to 1.68%
// overhead under logging load. We are safe because we check counts, but
// still need to clear null terminator
// memset(buffer, 0, sizeof(buffer));
- ssize_t n = recvmsg(socket, &hdr, 0);
+ ssize_t n = recvmsg(socket_, &hdr, 0);
if (n <= (ssize_t)(sizeof(android_log_header_t))) {
- return false;
+ return;
}
buffer[n] = 0;
@@ -75,14 +86,14 @@
}
if (cred == nullptr) {
- return false;
+ return;
}
if (cred->uid == AID_LOGD) {
// ignore log messages we send to ourself.
// Such log messages are often generated by libraries we depend on
// which use standard Android logging.
- return false;
+ return;
}
android_log_header_t* header =
@@ -90,13 +101,13 @@
log_id_t logId = static_cast<log_id_t>(header->id);
if (/* logId < LOG_ID_MIN || */ logId >= LOG_ID_MAX ||
logId == LOG_ID_KERNEL) {
- return false;
+ return;
}
if ((logId == LOG_ID_SECURITY) &&
(!__android_log_security() ||
!clientHasLogCredentials(cred->uid, cred->gid, cred->pid))) {
- return false;
+ return;
}
char* msg = ((char*)buffer) + sizeof(android_log_header_t);
@@ -105,16 +116,11 @@
// NB: hdr.msg_flags & MSG_TRUNC is not tested, silently passing a
// truncated message to the logs.
- int res = logbuf->log(logId, header->realtime, cred->uid, cred->pid, header->tid, msg,
- ((size_t)n <= UINT16_MAX) ? (uint16_t)n : UINT16_MAX);
- if (res > 0) {
- reader->notifyNewLog(static_cast<log_mask_t>(1 << logId));
- }
-
- return true;
+ logbuf_->Log(logId, header->realtime, cred->uid, cred->pid, header->tid, msg,
+ ((size_t)n <= UINT16_MAX) ? (uint16_t)n : UINT16_MAX);
}
-int LogListener::getLogSocket() {
+int LogListener::GetLogSocket() {
static const char socketName[] = "logdw";
int sock = android_get_control_socket(socketName);
diff --git a/logd/LogListener.h b/logd/LogListener.h
index 8fe3da4..c114e38 100644
--- a/logd/LogListener.h
+++ b/logd/LogListener.h
@@ -14,24 +14,20 @@
* limitations under the License.
*/
-#ifndef _LOGD_LOG_LISTENER_H__
-#define _LOGD_LOG_LISTENER_H__
+#pragma once
-#include <sysutils/SocketListener.h>
-#include "LogReader.h"
+#include "LogBuffer.h"
-class LogListener : public SocketListener {
- LogBuffer* logbuf;
- LogReader* reader;
+class LogListener {
+ public:
+ LogListener(LogBuffer* buf);
+ bool StartListener();
- public:
- LogListener(LogBuffer* buf, LogReader* reader);
+ private:
+ void ThreadFunction();
+ void HandleData();
+ static int GetLogSocket();
- protected:
- virtual bool onDataAvailable(SocketClient* cli);
-
- private:
- static int getLogSocket();
+ int socket_;
+ LogBuffer* logbuf_;
};
-
-#endif
diff --git a/logd/LogCommand.cpp b/logd/LogPermissions.cpp
similarity index 97%
rename from logd/LogCommand.cpp
rename to logd/LogPermissions.cpp
index 8bff9da..8f02d5a 100644
--- a/logd/LogCommand.cpp
+++ b/logd/LogPermissions.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "LogPermissions.h"
+
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
@@ -21,12 +23,6 @@
#include <private/android_filesystem_config.h>
-#include "LogCommand.h"
-#include "LogUtils.h"
-
-LogCommand::LogCommand(const char* cmd) : FrameworkCommand(cmd) {
-}
-
// gets a list of supplementary group IDs associated with
// the socket peer. This is implemented by opening
// /proc/PID/status and look for the "Group:" line.
diff --git a/logd/LogCommand.h b/logd/LogPermissions.h
similarity index 74%
rename from logd/LogCommand.h
rename to logd/LogPermissions.h
index e10ffa0..3130db5 100644
--- a/logd/LogCommand.h
+++ b/logd/LogPermissions.h
@@ -14,17 +14,11 @@
* limitations under the License.
*/
-#ifndef _LOGD_COMMAND_H
-#define _LOGD_COMMAND_H
+#pragma once
-#include <sysutils/FrameworkCommand.h>
+#include <sys/types.h>
+
#include <sysutils/SocketClient.h>
-class LogCommand : public FrameworkCommand {
- public:
- explicit LogCommand(const char* cmd);
- virtual ~LogCommand() {
- }
-};
-
-#endif
+bool clientHasLogCredentials(uid_t uid, gid_t gid, pid_t pid);
+bool clientHasLogCredentials(SocketClient* cli);
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
index 9db8c00..f71133d 100644
--- a/logd/LogReader.cpp
+++ b/logd/LogReader.cpp
@@ -21,26 +21,61 @@
#include <sys/socket.h>
#include <sys/types.h>
+#include <chrono>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
#include <cutils/sockets.h>
+#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
-#include "FlushCommand.h"
#include "LogBuffer.h"
#include "LogBufferElement.h"
+#include "LogPermissions.h"
#include "LogReader.h"
#include "LogUtils.h"
+#include "LogWriter.h"
-LogReader::LogReader(LogBuffer* logbuf)
- : SocketListener(getLogSocket(), true), mLogbuf(*logbuf) {
+static bool CanReadSecurityLogs(SocketClient* client) {
+ return client->getUid() == AID_SYSTEM || client->getGid() == AID_SYSTEM;
}
-// When we are notified a new log entry is available, inform
-// listening sockets who are watching this entry's log id.
-void LogReader::notifyNewLog(log_mask_t logMask) {
- FlushCommand command(*this, logMask);
- runOnEachSocket(&command);
+static std::string SocketClientToName(SocketClient* client) {
+ return android::base::StringPrintf("pid %d, fd %d", client->getPid(), client->getSocket());
}
+class SocketLogWriter : public LogWriter {
+ public:
+ SocketLogWriter(LogReader* reader, SocketClient* client, bool privileged)
+ : LogWriter(client->getUid(), privileged), reader_(reader), client_(client) {}
+
+ bool Write(const logger_entry& entry, const char* msg) override {
+ struct iovec iovec[2];
+ iovec[0].iov_base = const_cast<logger_entry*>(&entry);
+ iovec[0].iov_len = entry.hdr_size;
+ iovec[1].iov_base = const_cast<char*>(msg);
+ iovec[1].iov_len = entry.len;
+
+ return client_->sendDatav(iovec, 1 + (entry.len != 0)) == 0;
+ }
+
+ void Release() override {
+ reader_->release(client_);
+ client_->decRef();
+ }
+
+ void Shutdown() override { shutdown(client_->getSocket(), SHUT_RDWR); }
+
+ std::string name() const override { return SocketClientToName(client_); }
+
+ private:
+ LogReader* reader_;
+ SocketClient* client_;
+};
+
+LogReader::LogReader(LogBuffer* logbuf, LogReaderList* reader_list)
+ : SocketListener(getLogSocket(), true), log_buffer_(logbuf), reader_list_(reader_list) {}
+
// Note returning false will release the SocketClient instance.
bool LogReader::onDataAvailable(SocketClient* cli) {
static bool name_set;
@@ -53,22 +88,15 @@
int len = read(cli->getSocket(), buffer, sizeof(buffer) - 1);
if (len <= 0) {
- doSocketDelete(cli);
+ DoSocketDelete(cli);
return false;
}
buffer[len] = '\0';
- // Clients are only allowed to send one command, disconnect them if they
- // send another.
- LogTimeEntry::wrlock();
- for (const auto& entry : mLogbuf.mTimes) {
- if (entry->mClient == cli) {
- entry->release_Locked();
- LogTimeEntry::unlock();
- return false;
- }
+ // Clients are only allowed to send one command, disconnect them if they send another.
+ if (DoSocketDelete(cli)) {
+ return false;
}
- LogTimeEntry::unlock();
unsigned long tail = 0;
static const char _tail[] = " tail=";
@@ -85,12 +113,12 @@
start.strptime(cp + sizeof(_start) - 1, "%s.%q");
}
- uint64_t timeout = 0;
+ std::chrono::steady_clock::time_point deadline = {};
static const char _timeout[] = " timeout=";
cp = strstr(buffer, _timeout);
if (cp) {
- timeout = atol(cp + sizeof(_timeout) - 1) * NS_PER_SEC +
- log_time(CLOCK_REALTIME).nsec();
+ long timeout_seconds = atol(cp + sizeof(_timeout) - 1);
+ deadline = std::chrono::steady_clock::now() + std::chrono::seconds(timeout_seconds);
}
unsigned int logMask = -1;
@@ -124,133 +152,92 @@
if (!fastcmp<strncmp>(buffer, "dumpAndClose", 12)) {
// Allow writer to get some cycles, and wait for pending notifications
sched_yield();
- LogTimeEntry::wrlock();
- LogTimeEntry::unlock();
+ reader_list_->reader_threads_lock().lock();
+ reader_list_->reader_threads_lock().unlock();
sched_yield();
nonBlock = true;
}
- log_time sequence = start;
- //
- // This somewhat expensive data validation operation is required
- // for non-blocking, with timeout. The incoming timestamp must be
- // in range of the list, if not, return immediately. This is
- // used to prevent us from from getting stuck in timeout processing
- // with an invalid time.
- //
- // Find if time is really present in the logs, monotonic or real, implicit
- // conversion from monotonic or real as necessary to perform the check.
- // Exit in the check loop ASAP as you find a transition from older to
- // newer, but use the last entry found to ensure overlap.
- //
- if (nonBlock && (sequence != log_time::EPOCH) && timeout) {
- class LogFindStart { // A lambda by another name
- private:
- const pid_t mPid;
- const unsigned mLogMask;
- bool mStartTimeSet;
- log_time mStart;
- log_time& mSequence;
- log_time mLast;
- bool mIsMonotonic;
+ bool privileged = clientHasLogCredentials(cli);
+ bool can_read_security = CanReadSecurityLogs(cli);
+ if (!can_read_security) {
+ logMask &= ~(1 << LOG_ID_SECURITY);
+ }
- public:
- LogFindStart(pid_t pid, unsigned logMask, log_time& sequence,
- bool isMonotonic)
- : mPid(pid),
- mLogMask(logMask),
- mStartTimeSet(false),
- mStart(sequence),
- mSequence(sequence),
- mLast(sequence),
- mIsMonotonic(isMonotonic) {
+ std::unique_ptr<LogWriter> socket_log_writer(new SocketLogWriter(this, cli, privileged));
+
+ uint64_t sequence = 1;
+ // Convert realtime to sequence number
+ if (start != log_time::EPOCH) {
+ bool start_time_set = false;
+ uint64_t last = sequence;
+ auto log_find_start = [pid, start, &sequence, &start_time_set, &last](
+ log_id_t, pid_t element_pid, uint64_t element_sequence,
+ log_time element_realtime) -> FilterResult {
+ if (pid && pid != element_pid) {
+ return FilterResult::kSkip;
}
-
- static int callback(const LogBufferElement* element, void* obj) {
- LogFindStart* me = reinterpret_cast<LogFindStart*>(obj);
- if ((!me->mPid || (me->mPid == element->getPid())) &&
- (me->mLogMask & (1 << element->getLogId()))) {
- log_time real = element->getRealTime();
- if (me->mStart == real) {
- me->mSequence = real;
- me->mStartTimeSet = true;
- return -1;
- } else if (!me->mIsMonotonic || android::isMonotonic(real)) {
- if (me->mStart < real) {
- me->mSequence = me->mLast;
- me->mStartTimeSet = true;
- return -1;
- }
- me->mLast = real;
- } else {
- me->mLast = real;
- }
+ if (start == element_realtime) {
+ sequence = element_sequence;
+ start_time_set = true;
+ return FilterResult::kStop;
+ } else {
+ if (start < element_realtime) {
+ sequence = last;
+ start_time_set = true;
+ return FilterResult::kStop;
}
+ last = element_sequence;
+ }
+ return FilterResult::kSkip;
+ };
+ auto flush_to_state = log_buffer_->CreateFlushToState(sequence, logMask);
+ log_buffer_->FlushTo(socket_log_writer.get(), *flush_to_state, log_find_start);
+
+ if (!start_time_set) {
+ if (nonBlock) {
return false;
}
-
- bool found() {
- return mStartTimeSet;
- }
-
- } logFindStart(pid, logMask, sequence,
- logbuf().isMonotonic() && android::isMonotonic(start));
-
- logbuf().flushTo(cli, sequence, nullptr, FlushCommand::hasReadLogs(cli),
- FlushCommand::hasSecurityLogs(cli),
- logFindStart.callback, &logFindStart);
-
- if (!logFindStart.found()) {
- doSocketDelete(cli);
- return false;
+ sequence = log_buffer_->sequence();
}
}
- android::prdebug(
- "logdr: UID=%d GID=%d PID=%d %c tail=%lu logMask=%x pid=%d "
- "start=%" PRIu64 "ns timeout=%" PRIu64 "ns\n",
- cli->getUid(), cli->getGid(), cli->getPid(), nonBlock ? 'n' : 'b', tail,
- logMask, (int)pid, sequence.nsec(), timeout);
+ LOG(INFO) << android::base::StringPrintf(
+ "logdr: UID=%d GID=%d PID=%d %c tail=%lu logMask=%x pid=%d "
+ "start=%" PRIu64 "ns deadline=%" PRIi64 "ns",
+ cli->getUid(), cli->getGid(), cli->getPid(), nonBlock ? 'n' : 'b', tail, logMask,
+ (int)pid, start.nsec(), static_cast<int64_t>(deadline.time_since_epoch().count()));
- if (sequence == log_time::EPOCH) {
- timeout = 0;
+ if (start == log_time::EPOCH) {
+ deadline = {};
}
- LogTimeEntry::wrlock();
- auto entry = std::make_unique<LogTimeEntry>(
- *this, cli, nonBlock, tail, logMask, pid, sequence, timeout);
- if (!entry->startReader_Locked()) {
- LogTimeEntry::unlock();
- return false;
- }
-
+ auto lock = std::lock_guard{reader_list_->reader_threads_lock()};
+ auto entry = std::make_unique<LogReaderThread>(log_buffer_, reader_list_,
+ std::move(socket_log_writer), nonBlock, tail,
+ logMask, pid, start, sequence, deadline);
// release client and entry reference counts once done
cli->incRef();
- mLogbuf.mTimes.emplace_front(std::move(entry));
+ reader_list_->reader_threads().emplace_front(std::move(entry));
// Set acceptable upper limit to wait for slow reader processing b/27242723
struct timeval t = { LOGD_SNDTIMEO, 0 };
setsockopt(cli->getSocket(), SOL_SOCKET, SO_SNDTIMEO, (const char*)&t,
sizeof(t));
- LogTimeEntry::unlock();
-
return true;
}
-void LogReader::doSocketDelete(SocketClient* cli) {
- LastLogTimes& times = mLogbuf.mTimes;
- LogTimeEntry::wrlock();
- LastLogTimes::iterator it = times.begin();
- while (it != times.end()) {
- LogTimeEntry* entry = it->get();
- if (entry->mClient == cli) {
- entry->release_Locked();
- break;
+bool LogReader::DoSocketDelete(SocketClient* cli) {
+ auto cli_name = SocketClientToName(cli);
+ auto lock = std::lock_guard{reader_list_->reader_threads_lock()};
+ for (const auto& reader : reader_list_->reader_threads()) {
+ if (reader->name() == cli_name) {
+ reader->release_Locked();
+ return true;
}
- it++;
}
- LogTimeEntry::unlock();
+ return false;
}
int LogReader::getLogSocket() {
diff --git a/logd/LogReader.h b/logd/LogReader.h
index b5312b6..b85a584 100644
--- a/logd/LogReader.h
+++ b/logd/LogReader.h
@@ -14,35 +14,28 @@
* limitations under the License.
*/
-#ifndef _LOGD_LOG_WRITER_H__
-#define _LOGD_LOG_WRITER_H__
+#pragma once
#include <sysutils/SocketListener.h>
-#include "LogTimes.h"
+#include "LogBuffer.h"
+#include "LogReaderList.h"
+#include "LogReaderThread.h"
#define LOGD_SNDTIMEO 32
-class LogBuffer;
-
class LogReader : public SocketListener {
- LogBuffer& mLogbuf;
+ public:
+ explicit LogReader(LogBuffer* logbuf, LogReaderList* reader_list);
- public:
- explicit LogReader(LogBuffer* logbuf);
- void notifyNewLog(log_mask_t logMask);
-
- LogBuffer& logbuf(void) const {
- return mLogbuf;
- }
-
- protected:
+ protected:
virtual bool onDataAvailable(SocketClient* cli);
- private:
+ private:
static int getLogSocket();
- void doSocketDelete(SocketClient* cli);
-};
+ bool DoSocketDelete(SocketClient* cli);
-#endif
+ LogBuffer* log_buffer_;
+ LogReaderList* reader_list_;
+};
diff --git a/logd/LogReaderList.cpp b/logd/LogReaderList.cpp
new file mode 100644
index 0000000..32ba291
--- /dev/null
+++ b/logd/LogReaderList.cpp
@@ -0,0 +1,33 @@
+/*
+ * 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 "LogReaderList.h"
+
+// When we are notified a new log entry is available, inform
+// listening sockets who are watching this entry's log id.
+void LogReaderList::NotifyNewLog(LogMask log_mask) const {
+ auto lock = std::lock_guard{reader_threads_lock_};
+
+ for (const auto& entry : reader_threads_) {
+ if (!entry->IsWatchingMultiple(log_mask)) {
+ continue;
+ }
+ if (entry->deadline().time_since_epoch().count() != 0) {
+ continue;
+ }
+ entry->triggerReader_Locked();
+ }
+}
diff --git a/init/service_lock.h b/logd/LogReaderList.h
similarity index 60%
rename from init/service_lock.h
rename to logd/LogReaderList.h
index 6b94271..594716a 100644
--- a/init/service_lock.h
+++ b/logd/LogReaderList.h
@@ -16,25 +16,21 @@
#pragma once
+#include <list>
+#include <memory>
#include <mutex>
-#include <android-base/thread_annotations.h>
+#include "LogBuffer.h"
+#include "LogReaderThread.h"
-namespace android {
-namespace init {
-
-// This class exists to add thread annotations, since they're absent from std::recursive_mutex.
-
-class CAPABILITY("mutex") RecursiveMutex {
+class LogReaderList {
public:
- void lock() ACQUIRE() { mutex_.lock(); }
- void unlock() RELEASE() { mutex_.unlock(); }
+ void NotifyNewLog(LogMask log_mask) const;
+
+ std::list<std::unique_ptr<LogReaderThread>>& reader_threads() { return reader_threads_; }
+ std::mutex& reader_threads_lock() { return reader_threads_lock_; }
private:
- std::recursive_mutex mutex_;
-};
-
-extern RecursiveMutex service_lock;
-
-} // namespace init
-} // namespace android
+ std::list<std::unique_ptr<LogReaderThread>> reader_threads_;
+ mutable std::mutex reader_threads_lock_;
+};
\ No newline at end of file
diff --git a/logd/LogReaderThread.cpp b/logd/LogReaderThread.cpp
new file mode 100644
index 0000000..dc8582d
--- /dev/null
+++ b/logd/LogReaderThread.cpp
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LogReaderThread.h"
+
+#include <errno.h>
+#include <string.h>
+#include <sys/prctl.h>
+
+#include <thread>
+
+#include "LogBuffer.h"
+#include "LogReaderList.h"
+
+using namespace std::placeholders;
+
+LogReaderThread::LogReaderThread(LogBuffer* log_buffer, LogReaderList* reader_list,
+ std::unique_ptr<LogWriter> writer, bool non_block,
+ unsigned long tail, LogMask log_mask, pid_t pid,
+ log_time start_time, uint64_t start,
+ std::chrono::steady_clock::time_point deadline)
+ : log_buffer_(log_buffer),
+ reader_list_(reader_list),
+ writer_(std::move(writer)),
+ pid_(pid),
+ tail_(tail),
+ count_(0),
+ index_(0),
+ start_time_(start_time),
+ deadline_(deadline),
+ non_block_(non_block) {
+ cleanSkip_Locked();
+ flush_to_state_ = log_buffer_->CreateFlushToState(start, log_mask);
+ auto thread = std::thread{&LogReaderThread::ThreadFunction, this};
+ thread.detach();
+}
+
+void LogReaderThread::ThreadFunction() {
+ prctl(PR_SET_NAME, "logd.reader.per");
+
+ auto lock = std::unique_lock{reader_list_->reader_threads_lock()};
+
+ while (!release_) {
+ if (deadline_.time_since_epoch().count() != 0) {
+ if (thread_triggered_condition_.wait_until(lock, deadline_) ==
+ std::cv_status::timeout) {
+ deadline_ = {};
+ }
+ if (release_) {
+ break;
+ }
+ }
+
+ lock.unlock();
+
+ if (tail_) {
+ auto first_pass_state = log_buffer_->CreateFlushToState(flush_to_state_->start(),
+ flush_to_state_->log_mask());
+ log_buffer_->FlushTo(
+ writer_.get(), *first_pass_state,
+ [this](log_id_t log_id, pid_t pid, uint64_t sequence, log_time realtime) {
+ return FilterFirstPass(log_id, pid, sequence, realtime);
+ });
+ }
+ bool flush_success = log_buffer_->FlushTo(
+ writer_.get(), *flush_to_state_,
+ [this](log_id_t log_id, pid_t pid, uint64_t sequence, log_time realtime) {
+ return FilterSecondPass(log_id, pid, sequence, realtime);
+ });
+
+ // We only ignore entries before the original start time for the first flushTo(), if we
+ // get entries after this first flush before the original start time, then the client
+ // wouldn't have seen them.
+ // Note: this is still racy and may skip out of order events that came in since the last
+ // time the client disconnected and then reconnected with the new start time. The long term
+ // solution here is that clients must request events since a specific sequence number.
+ start_time_.tv_sec = 0;
+ start_time_.tv_nsec = 0;
+
+ lock.lock();
+
+ if (!flush_success) {
+ break;
+ }
+
+ if (non_block_ || release_) {
+ break;
+ }
+
+ cleanSkip_Locked();
+
+ if (deadline_.time_since_epoch().count() == 0) {
+ thread_triggered_condition_.wait(lock);
+ }
+ }
+
+ writer_->Release();
+
+ auto& log_reader_threads = reader_list_->reader_threads();
+ auto it = std::find_if(log_reader_threads.begin(), log_reader_threads.end(),
+ [this](const auto& other) { return other.get() == this; });
+
+ if (it != log_reader_threads.end()) {
+ log_reader_threads.erase(it);
+ }
+}
+
+// A first pass to count the number of elements
+FilterResult LogReaderThread::FilterFirstPass(log_id_t, pid_t pid, uint64_t, log_time realtime) {
+ auto lock = std::lock_guard{reader_list_->reader_threads_lock()};
+
+ if ((!pid_ || pid_ == pid) && (start_time_ == log_time::EPOCH || start_time_ <= realtime)) {
+ ++count_;
+ }
+
+ return FilterResult::kSkip;
+}
+
+// A second pass to send the selected elements
+FilterResult LogReaderThread::FilterSecondPass(log_id_t log_id, pid_t pid, uint64_t,
+ log_time realtime) {
+ auto lock = std::lock_guard{reader_list_->reader_threads_lock()};
+
+ if (skip_ahead_[log_id]) {
+ skip_ahead_[log_id]--;
+ return FilterResult::kSkip;
+ }
+
+ // Truncate to close race between first and second pass
+ if (non_block_ && tail_ && index_ >= count_) {
+ return FilterResult::kStop;
+ }
+
+ if (pid_ && pid_ != pid) {
+ return FilterResult::kSkip;
+ }
+
+ if (start_time_ != log_time::EPOCH && realtime <= start_time_) {
+ return FilterResult::kSkip;
+ }
+
+ if (release_) {
+ return FilterResult::kStop;
+ }
+
+ if (!tail_) {
+ goto ok;
+ }
+
+ ++index_;
+
+ if (count_ > tail_ && index_ <= (count_ - tail_)) {
+ return FilterResult::kSkip;
+ }
+
+ if (!non_block_) {
+ tail_ = 0;
+ }
+
+ok:
+ if (!skip_ahead_[log_id]) {
+ return FilterResult::kWrite;
+ }
+ return FilterResult::kSkip;
+}
+
+void LogReaderThread::cleanSkip_Locked(void) {
+ memset(skip_ahead_, 0, sizeof(skip_ahead_));
+}
diff --git a/logd/LogReaderThread.h b/logd/LogReaderThread.h
new file mode 100644
index 0000000..bf70b94
--- /dev/null
+++ b/logd/LogReaderThread.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2012-2013 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>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include <chrono>
+#include <condition_variable>
+#include <list>
+#include <memory>
+
+#include <log/log.h>
+#include <sysutils/SocketClient.h>
+
+#include "LogBuffer.h"
+#include "LogWriter.h"
+
+class LogReaderList;
+
+class LogReaderThread {
+ public:
+ LogReaderThread(LogBuffer* log_buffer, LogReaderList* reader_list,
+ std::unique_ptr<LogWriter> writer, bool non_block, unsigned long tail,
+ LogMask log_mask, pid_t pid, log_time start_time, uint64_t sequence,
+ std::chrono::steady_clock::time_point deadline);
+ void triggerReader_Locked() { thread_triggered_condition_.notify_all(); }
+
+ void triggerSkip_Locked(log_id_t id, unsigned int skip) { skip_ahead_[id] = skip; }
+ void cleanSkip_Locked();
+
+ void release_Locked() {
+ // gracefully shut down the socket.
+ writer_->Shutdown();
+ release_ = true;
+ thread_triggered_condition_.notify_all();
+ }
+
+ bool IsWatching(log_id_t id) const { return flush_to_state_->log_mask() & (1 << id); }
+ bool IsWatchingMultiple(LogMask log_mask) const {
+ return flush_to_state_->log_mask() & log_mask;
+ }
+
+ std::string name() const { return writer_->name(); }
+ uint64_t start() const { return flush_to_state_->start(); }
+ std::chrono::steady_clock::time_point deadline() const { return deadline_; }
+
+ private:
+ void ThreadFunction();
+ // flushTo filter callbacks
+ FilterResult FilterFirstPass(log_id_t log_id, pid_t pid, uint64_t sequence, log_time realtime);
+ FilterResult FilterSecondPass(log_id_t log_id, pid_t pid, uint64_t sequence, log_time realtime);
+
+ std::condition_variable thread_triggered_condition_;
+ LogBuffer* log_buffer_;
+ LogReaderList* reader_list_;
+ std::unique_ptr<LogWriter> writer_;
+
+ // Set to true to cause the thread to end and the LogReaderThread to delete itself.
+ bool release_ = false;
+
+ // If set to non-zero, only pids equal to this are read by the reader.
+ const pid_t pid_;
+ // When a reader is referencing (via start_) old elements in the log buffer, and the log
+ // buffer's size grows past its memory limit, the log buffer may request the reader to skip
+ // ahead a specified number of logs.
+ unsigned int skip_ahead_[LOG_ID_MAX];
+ // LogBuffer::FlushTo() needs to store state across subsequent calls.
+ std::unique_ptr<FlushToState> flush_to_state_;
+
+ // These next three variables are used for reading only the most recent lines aka `adb logcat
+ // -t` / `adb logcat -T`.
+ // tail_ is the number of most recent lines to print.
+ unsigned long tail_;
+ // count_ is the result of a first pass through the log buffer to determine how many total
+ // messages there are.
+ unsigned long count_;
+ // index_ is used along with count_ to only start sending lines once index_ > (count_ - tail_)
+ // and to disconnect the reader (if it is dumpAndClose, `adb logcat -t`), when index_ >= count_.
+ unsigned long index_;
+
+ // When a reader requests logs starting from a given timestamp, its stored here for the first
+ // pass, such that logs before this time stamp that are accumulated in the buffer are ignored.
+ log_time start_time_;
+ // CLOCK_MONOTONIC based deadline used for log wrapping. If this deadline expires before logs
+ // wrap, then wake up and send the logs to the reader anyway.
+ std::chrono::steady_clock::time_point deadline_;
+ // If this reader is 'dumpAndClose' and will disconnect once it has read its intended logs.
+ const bool non_block_;
+};
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index 431b778..d49982a 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "LogStatistics.h"
+
#include <ctype.h>
#include <fcntl.h>
#include <inttypes.h>
@@ -27,14 +29,38 @@
#include <private/android_logger.h>
-#include "LogStatistics.h"
+#include "LogBufferElement.h"
static const uint64_t hourSec = 60 * 60;
static const uint64_t monthSec = 31 * 24 * hourSec;
-size_t LogStatistics::SizesTotal;
+std::atomic<size_t> LogStatistics::SizesTotal;
-LogStatistics::LogStatistics() : enable(false) {
+static std::string TagNameKey(const LogStatisticsElement& element) {
+ if (IsBinary(element.log_id)) {
+ uint32_t tag = element.tag;
+ if (tag) {
+ const char* cp = android::tagToName(tag);
+ if (cp) {
+ return std::string(cp);
+ }
+ }
+ return android::base::StringPrintf("[%" PRIu32 "]", tag);
+ }
+ const char* msg = element.msg;
+ if (!msg) {
+ return "chatty";
+ }
+ ++msg;
+ uint16_t len = element.msg_len;
+ len = (len <= 1) ? 0 : strnlen(msg, len - 1);
+ if (!len) {
+ return "<NULL>";
+ }
+ return std::string(msg, len);
+}
+
+LogStatistics::LogStatistics(bool enable_statistics) : enable(enable_statistics) {
log_time now(CLOCK_REALTIME);
log_id_for_each(id) {
mSizes[id] = 0;
@@ -79,19 +105,18 @@
}
}
-void LogStatistics::addTotal(LogBufferElement* element) {
- if (element->getDropped()) return;
+void LogStatistics::AddTotal(log_id_t log_id, uint16_t size) {
+ auto lock = std::lock_guard{lock_};
- log_id_t log_id = element->getLogId();
- uint16_t size = element->getMsgLen();
mSizesTotal[log_id] += size;
SizesTotal += size;
++mElementsTotal[log_id];
}
-void LogStatistics::add(LogBufferElement* element) {
- log_id_t log_id = element->getLogId();
- uint16_t size = element->getMsgLen();
+void LogStatistics::Add(const LogStatisticsElement& element) {
+ auto lock = std::lock_guard{lock_};
+ log_id_t log_id = element.log_id;
+ uint16_t size = element.msg_len;
mSizes[log_id] += size;
++mElements[log_id];
@@ -100,7 +125,7 @@
// evaluated and trimmed, thus recording size and number of
// elements, but we must recognize the manufactured dropped
// entry as not contributing to the lifetime totals.
- if (element->getDropped()) {
+ if (element.dropped_count) {
++mDroppedElements[log_id];
} else {
mSizesTotal[log_id] += size;
@@ -108,7 +133,7 @@
++mElementsTotal[log_id];
}
- log_time stamp(element->getRealTime());
+ log_time stamp(element.realtime);
if (mNewest[log_id] < stamp) {
// A major time update invalidates the statistics :-(
log_time diff = stamp - mNewest[log_id];
@@ -133,114 +158,120 @@
return;
}
- uidTable[log_id].add(element->getUid(), element);
- if (element->getUid() == AID_SYSTEM) {
- pidSystemTable[log_id].add(element->getPid(), element);
+ uidTable[log_id].Add(element.uid, element);
+ if (element.uid == AID_SYSTEM) {
+ pidSystemTable[log_id].Add(element.pid, element);
}
if (!enable) {
return;
}
- pidTable.add(element->getPid(), element);
- tidTable.add(element->getTid(), element);
+ pidTable.Add(element.pid, element);
+ tidTable.Add(element.tid, element);
- uint32_t tag = element->getTag();
+ uint32_t tag = element.tag;
if (tag) {
if (log_id == LOG_ID_SECURITY) {
- securityTagTable.add(tag, element);
+ securityTagTable.Add(tag, element);
} else {
- tagTable.add(tag, element);
+ tagTable.Add(tag, element);
}
}
- if (!element->getDropped()) {
- tagNameTable.add(TagNameKey(element), element);
+ if (!element.dropped_count) {
+ tagNameTable.Add(TagNameKey(element), element);
}
}
-void LogStatistics::subtract(LogBufferElement* element) {
- log_id_t log_id = element->getLogId();
- uint16_t size = element->getMsgLen();
+void LogStatistics::Subtract(const LogStatisticsElement& element) {
+ auto lock = std::lock_guard{lock_};
+ log_id_t log_id = element.log_id;
+ uint16_t size = element.msg_len;
mSizes[log_id] -= size;
--mElements[log_id];
- if (element->getDropped()) {
+ if (element.dropped_count) {
--mDroppedElements[log_id];
}
- if (mOldest[log_id] < element->getRealTime()) {
- mOldest[log_id] = element->getRealTime();
+ if (mOldest[log_id] < element.realtime) {
+ mOldest[log_id] = element.realtime;
}
if (log_id == LOG_ID_KERNEL) {
return;
}
- uidTable[log_id].subtract(element->getUid(), element);
- if (element->getUid() == AID_SYSTEM) {
- pidSystemTable[log_id].subtract(element->getPid(), element);
+ uidTable[log_id].Subtract(element.uid, element);
+ if (element.uid == AID_SYSTEM) {
+ pidSystemTable[log_id].Subtract(element.pid, element);
}
if (!enable) {
return;
}
- pidTable.subtract(element->getPid(), element);
- tidTable.subtract(element->getTid(), element);
+ pidTable.Subtract(element.pid, element);
+ tidTable.Subtract(element.tid, element);
- uint32_t tag = element->getTag();
+ uint32_t tag = element.tag;
if (tag) {
if (log_id == LOG_ID_SECURITY) {
- securityTagTable.subtract(tag, element);
+ securityTagTable.Subtract(tag, element);
} else {
- tagTable.subtract(tag, element);
+ tagTable.Subtract(tag, element);
}
}
- if (!element->getDropped()) {
- tagNameTable.subtract(TagNameKey(element), element);
+ if (!element.dropped_count) {
+ tagNameTable.Subtract(TagNameKey(element), element);
}
}
// Atomically set an entry to drop
// entry->setDropped(1) must follow this call, caller should do this explicitly.
-void LogStatistics::drop(LogBufferElement* element) {
- log_id_t log_id = element->getLogId();
- uint16_t size = element->getMsgLen();
+void LogStatistics::Drop(const LogStatisticsElement& element) {
+ auto lock = std::lock_guard{lock_};
+ log_id_t log_id = element.log_id;
+ uint16_t size = element.msg_len;
mSizes[log_id] -= size;
++mDroppedElements[log_id];
- if (mNewestDropped[log_id] < element->getRealTime()) {
- mNewestDropped[log_id] = element->getRealTime();
+ if (mNewestDropped[log_id] < element.realtime) {
+ mNewestDropped[log_id] = element.realtime;
}
- uidTable[log_id].drop(element->getUid(), element);
- if (element->getUid() == AID_SYSTEM) {
- pidSystemTable[log_id].drop(element->getPid(), element);
+ uidTable[log_id].Drop(element.uid, element);
+ if (element.uid == AID_SYSTEM) {
+ pidSystemTable[log_id].Drop(element.pid, element);
}
if (!enable) {
return;
}
- pidTable.drop(element->getPid(), element);
- tidTable.drop(element->getTid(), element);
+ pidTable.Drop(element.pid, element);
+ tidTable.Drop(element.tid, element);
- uint32_t tag = element->getTag();
+ uint32_t tag = element.tag;
if (tag) {
if (log_id == LOG_ID_SECURITY) {
- securityTagTable.drop(tag, element);
+ securityTagTable.Drop(tag, element);
} else {
- tagTable.drop(tag, element);
+ tagTable.Drop(tag, element);
}
}
- tagNameTable.subtract(TagNameKey(element), element);
+ tagNameTable.Subtract(TagNameKey(element), element);
+}
+
+const char* LogStatistics::UidToName(uid_t uid) const {
+ auto lock = std::lock_guard{lock_};
+ return UidToNameLocked(uid);
}
// caller must own and free character string
-// Requires parent LogBuffer::wrlock() to be held
-const char* LogStatistics::uidToName(uid_t uid) const {
+const char* LogStatistics::UidToNameLocked(uid_t uid) const {
// Local hard coded favourites
if (uid == AID_LOGD) {
return strdup("auditd");
@@ -278,8 +309,8 @@
++it) {
const PidEntry& entry = it->second;
- if (entry.getUid() == uid) {
- const char* nameTmp = entry.getName();
+ if (entry.uid() == uid) {
+ const char* nameTmp = entry.name();
if (nameTmp) {
if (!name) {
@@ -297,6 +328,82 @@
return name;
}
+template <typename TKey, typename TEntry>
+void LogStatistics::WorstTwoWithThreshold(const LogHashtable<TKey, TEntry>& table, size_t threshold,
+ int* worst, size_t* worst_sizes,
+ size_t* second_worst_sizes) const {
+ std::array<const TKey*, 2> max_keys;
+ std::array<const TEntry*, 2> max_entries;
+ table.MaxEntries(AID_ROOT, 0, max_keys, max_entries);
+ if (max_entries[0] == nullptr || max_entries[1] == nullptr) {
+ return;
+ }
+ *worst_sizes = max_entries[0]->getSizes();
+ // b/24782000: Allow time horizon to extend roughly tenfold, assume average entry length is
+ // 100 characters.
+ if (*worst_sizes > threshold && *worst_sizes > (10 * max_entries[0]->dropped_count())) {
+ *worst = *max_keys[0];
+ *second_worst_sizes = max_entries[1]->getSizes();
+ if (*second_worst_sizes < threshold) {
+ *second_worst_sizes = threshold;
+ }
+ }
+}
+
+void LogStatistics::WorstTwoUids(log_id id, size_t threshold, int* worst, size_t* worst_sizes,
+ size_t* second_worst_sizes) const {
+ auto lock = std::lock_guard{lock_};
+ WorstTwoWithThreshold(uidTable[id], threshold, worst, worst_sizes, second_worst_sizes);
+}
+
+void LogStatistics::WorstTwoTags(size_t threshold, int* worst, size_t* worst_sizes,
+ size_t* second_worst_sizes) const {
+ auto lock = std::lock_guard{lock_};
+ WorstTwoWithThreshold(tagTable, threshold, worst, worst_sizes, second_worst_sizes);
+}
+
+void LogStatistics::WorstTwoSystemPids(log_id id, size_t worst_uid_sizes, int* worst,
+ size_t* second_worst_sizes) const {
+ auto lock = std::lock_guard{lock_};
+ std::array<const pid_t*, 2> max_keys;
+ std::array<const PidEntry*, 2> max_entries;
+ pidSystemTable[id].MaxEntries(AID_SYSTEM, 0, max_keys, max_entries);
+ if (max_entries[0] == nullptr || max_entries[1] == nullptr) {
+ return;
+ }
+
+ *worst = *max_keys[0];
+ *second_worst_sizes = worst_uid_sizes - max_entries[0]->getSizes() + max_entries[1]->getSizes();
+}
+
+// Prune at most 10% of the log entries or maxPrune, whichever is less.
+bool LogStatistics::ShouldPrune(log_id id, unsigned long max_size,
+ unsigned long* prune_rows) const {
+ static constexpr size_t kMinPrune = 4;
+ static constexpr size_t kMaxPrune = 256;
+
+ auto lock = std::lock_guard{lock_};
+ size_t sizes = mSizes[id];
+ if (sizes <= max_size) {
+ return false;
+ }
+ size_t size_over = sizes - ((max_size * 9) / 10);
+ size_t elements = mElements[id] - mDroppedElements[id];
+ size_t min_elements = elements / 100;
+ if (min_elements < kMinPrune) {
+ min_elements = kMinPrune;
+ }
+ *prune_rows = elements * size_over / sizes;
+ if (*prune_rows < min_elements) {
+ *prune_rows = min_elements;
+ }
+ if (*prune_rows > kMaxPrune) {
+ *prune_rows = kMaxPrune;
+ }
+
+ return true;
+}
+
std::string UidEntry::formatHeader(const std::string& name, log_id_t id) const {
bool isprune = worstUidEnabledForLogid(id);
return formatLine(android::base::StringPrintf(name.c_str(),
@@ -308,15 +415,14 @@
}
// Helper to truncate name, if too long, and add name dressings
-static void formatTmp(const LogStatistics& stat, const char* nameTmp, uid_t uid,
- std::string& name, std::string& size, size_t nameLen) {
+void LogStatistics::FormatTmp(const char* nameTmp, uid_t uid, std::string& name, std::string& size,
+ size_t nameLen) const {
const char* allocNameTmp = nullptr;
- if (!nameTmp) nameTmp = allocNameTmp = stat.uidToName(uid);
+ if (!nameTmp) nameTmp = allocNameTmp = UidToNameLocked(uid);
if (nameTmp) {
size_t lenSpace = std::max(nameLen - name.length(), (size_t)1);
- size_t len = EntryBaseConstants::total_len -
- EntryBaseConstants::pruned_len - size.length() -
- name.length() - lenSpace - 2;
+ size_t len = EntryBase::TOTAL_LEN - EntryBase::PRUNED_LEN - size.length() - name.length() -
+ lenSpace - 2;
size_t lenNameTmp = strlen(nameTmp);
while ((len < lenNameTmp) && (lenSpace > 1)) {
++len;
@@ -332,12 +438,12 @@
}
}
-std::string UidEntry::format(const LogStatistics& stat, log_id_t id) const {
- uid_t uid = getUid();
+std::string UidEntry::format(const LogStatistics& stat, log_id_t id, uid_t uid) const
+ REQUIRES(stat.lock_) {
std::string name = android::base::StringPrintf("%u", uid);
std::string size = android::base::StringPrintf("%zu", getSizes());
- formatTmp(stat, nullptr, uid, name, size, 6);
+ stat.FormatTmp(nullptr, uid, name, size, 6);
std::string pruned = "";
if (worstUidEnabledForLogid(id)) {
@@ -345,17 +451,17 @@
for (LogStatistics::uidTable_t::const_iterator it =
stat.uidTable[id].begin();
it != stat.uidTable[id].end(); ++it) {
- totalDropped += it->second.getDropped();
+ totalDropped += it->second.dropped_count();
}
- size_t sizes = stat.sizes(id);
- size_t totalSize = stat.sizesTotal(id);
- size_t totalElements = stat.elementsTotal(id);
+ size_t sizes = stat.mSizes[id];
+ size_t totalSize = stat.mSizesTotal[id];
+ size_t totalElements = stat.mElementsTotal[id];
float totalVirtualSize =
(float)sizes + (float)totalDropped * totalSize / totalElements;
size_t entrySize = getSizes();
float virtualEntrySize = entrySize;
int realPermille = virtualEntrySize * 1000.0 / sizes;
- size_t dropped = getDropped();
+ size_t dropped = dropped_count();
if (dropped) {
pruned = android::base::StringPrintf("%zu", dropped);
virtualEntrySize += (float)dropped * totalSize / totalElements;
@@ -382,8 +488,7 @@
change = android::base::StringPrintf(
"%s%d%s", prefix, (permille + 5) / 10, units);
}
- ssize_t spaces = EntryBaseConstants::pruned_len - 2 -
- pruned.length() - change.length();
+ ssize_t spaces = EntryBase::PRUNED_LEN - 2 - pruned.length() - change.length();
if ((spaces <= 0) && pruned.length()) {
spaces = 1;
}
@@ -401,27 +506,25 @@
}
static const size_t maximum_sorted_entries = 32;
- std::unique_ptr<const PidEntry* []> sorted =
- stat.pidSystemTable[id].sort(uid, (pid_t)0, maximum_sorted_entries);
+ std::array<const pid_t*, maximum_sorted_entries> sorted_pids;
+ std::array<const PidEntry*, maximum_sorted_entries> sorted_entries;
+ stat.pidSystemTable[id].MaxEntries(uid, 0, sorted_pids, sorted_entries);
- if (!sorted.get()) {
- return output;
- }
std::string byPid;
size_t index;
bool hasDropped = false;
for (index = 0; index < maximum_sorted_entries; ++index) {
- const PidEntry* entry = sorted[index];
+ const PidEntry* entry = sorted_entries[index];
if (!entry) {
break;
}
if (entry->getSizes() <= (getSizes() / 100)) {
break;
}
- if (entry->getDropped()) {
+ if (entry->dropped_count()) {
hasDropped = true;
}
- byPid += entry->format(stat, id);
+ byPid += entry->format(stat, id, *sorted_pids[index]);
}
if (index > 1) { // print this only if interesting
std::string ditto("\" ");
@@ -440,17 +543,15 @@
std::string("BYTES"), std::string("NUM"));
}
-std::string PidEntry::format(const LogStatistics& stat,
- log_id_t /* id */) const {
- uid_t uid = getUid();
- pid_t pid = getPid();
- std::string name = android::base::StringPrintf("%5u/%u", pid, uid);
+std::string PidEntry::format(const LogStatistics& stat, log_id_t, pid_t pid) const
+ REQUIRES(stat.lock_) {
+ std::string name = android::base::StringPrintf("%5u/%u", pid, uid_);
std::string size = android::base::StringPrintf("%zu", getSizes());
- formatTmp(stat, getName(), uid, name, size, 12);
+ stat.FormatTmp(name_, uid_, name, size, 12);
std::string pruned = "";
- size_t dropped = getDropped();
+ size_t dropped = dropped_count();
if (dropped) {
pruned = android::base::StringPrintf("%zu", dropped);
}
@@ -465,16 +566,15 @@
std::string("NUM"));
}
-std::string TidEntry::format(const LogStatistics& stat,
- log_id_t /* id */) const {
- uid_t uid = getUid();
- std::string name = android::base::StringPrintf("%5u/%u", getTid(), uid);
+std::string TidEntry::format(const LogStatistics& stat, log_id_t, pid_t tid) const
+ REQUIRES(stat.lock_) {
+ std::string name = android::base::StringPrintf("%5u/%u", tid, uid_);
std::string size = android::base::StringPrintf("%zu", getSizes());
- formatTmp(stat, getName(), uid, name, size, 12);
+ stat.FormatTmp(name_, uid_, name, size, 12);
std::string pruned = "";
- size_t dropped = getDropped();
+ size_t dropped = dropped_count();
if (dropped) {
pruned = android::base::StringPrintf("%zu", dropped);
}
@@ -490,16 +590,14 @@
std::string("BYTES"), std::string(isprune ? "NUM" : ""));
}
-std::string TagEntry::format(const LogStatistics& /* stat */,
- log_id_t /* id */) const {
+std::string TagEntry::format(const LogStatistics&, log_id_t, uint32_t) const {
std::string name;
- uid_t uid = getUid();
- if (uid == (uid_t)-1) {
- name = android::base::StringPrintf("%7u", getKey());
+ if (uid_ == (uid_t)-1) {
+ name = android::base::StringPrintf("%7u", key());
} else {
- name = android::base::StringPrintf("%7u/%u", getKey(), uid);
+ name = android::base::StringPrintf("%7u/%u", key(), uid_);
}
- const char* nameTmp = getName();
+ const char* nameTmp = this->name();
if (nameTmp) {
name += android::base::StringPrintf(
"%*s%s", (int)std::max(14 - name.length(), (size_t)1), "", nameTmp);
@@ -508,7 +606,7 @@
std::string size = android::base::StringPrintf("%zu", getSizes());
std::string pruned = "";
- size_t dropped = getDropped();
+ size_t dropped = dropped_count();
if (dropped) {
pruned = android::base::StringPrintf("%zu", dropped);
}
@@ -523,37 +621,33 @@
std::string("BYTES"), std::string(""));
}
-std::string TagNameEntry::format(const LogStatistics& /* stat */,
- log_id_t /* id */) const {
+std::string TagNameEntry::format(const LogStatistics&, log_id_t,
+ const std::string& key_name) const {
std::string name;
- pid_t tid = getTid();
- pid_t pid = getPid();
std::string pidstr;
- if (pid != (pid_t)-1) {
- pidstr = android::base::StringPrintf("%u", pid);
- if ((tid != (pid_t)-1) && (tid != pid)) pidstr = "/" + pidstr;
+ if (pid_ != (pid_t)-1) {
+ pidstr = android::base::StringPrintf("%u", pid_);
+ if (tid_ != (pid_t)-1 && tid_ != pid_) pidstr = "/" + pidstr;
}
int len = 9 - pidstr.length();
if (len < 0) len = 0;
- if ((tid == (pid_t)-1) || (tid == pid)) {
+ if (tid_ == (pid_t)-1 || tid_ == pid_) {
name = android::base::StringPrintf("%*s", len, "");
} else {
- name = android::base::StringPrintf("%*u", len, tid);
+ name = android::base::StringPrintf("%*u", len, tid_);
}
name += pidstr;
- uid_t uid = getUid();
- if (uid != (uid_t)-1) {
- name += android::base::StringPrintf("/%u", uid);
+ if (uid_ != (uid_t)-1) {
+ name += android::base::StringPrintf("/%u", uid_);
}
std::string size = android::base::StringPrintf("%zu", getSizes());
- const char* nameTmp = getName();
+ const char* nameTmp = key_name.data();
if (nameTmp) {
size_t lenSpace = std::max(16 - name.length(), (size_t)1);
- size_t len = EntryBaseConstants::total_len -
- EntryBaseConstants::pruned_len - size.length() -
- name.length() - lenSpace - 2;
+ size_t len = EntryBase::TOTAL_LEN - EntryBase::PRUNED_LEN - size.length() - name.length() -
+ lenSpace - 2;
size_t lenNameTmp = strlen(nameTmp);
while ((len < lenNameTmp) && (lenSpace > 1)) {
++len;
@@ -611,8 +705,37 @@
return output;
}
-std::string LogStatistics::format(uid_t uid, pid_t pid,
- unsigned int logMask) const {
+template <typename TKey, typename TEntry>
+std::string LogStatistics::FormatTable(const LogHashtable<TKey, TEntry>& table, uid_t uid,
+ pid_t pid, const std::string& name, log_id_t id) const
+ REQUIRES(lock_) {
+ static const size_t maximum_sorted_entries = 32;
+ std::string output;
+ std::array<const TKey*, maximum_sorted_entries> sorted_keys;
+ std::array<const TEntry*, maximum_sorted_entries> sorted_entries;
+ table.MaxEntries(uid, pid, sorted_keys, sorted_entries);
+ bool header_printed = false;
+ for (size_t index = 0; index < maximum_sorted_entries; ++index) {
+ const TEntry* entry = sorted_entries[index];
+ if (!entry) {
+ break;
+ }
+ if (entry->getSizes() <= (sorted_entries[0]->getSizes() / 100)) {
+ break;
+ }
+ if (!header_printed) {
+ output += "\n\n";
+ output += entry->formatHeader(name, id);
+ header_printed = true;
+ }
+ output += entry->format(*this, id, *sorted_keys[index]);
+ }
+ return output;
+}
+
+std::string LogStatistics::Format(uid_t uid, pid_t pid, unsigned int logMask) const {
+ auto lock = std::lock_guard{lock_};
+
static const uint16_t spaces_total = 19;
// Report on total logging, current and for all time
@@ -642,9 +765,9 @@
if (!(logMask & (1 << id))) continue;
oldLength = output.length();
if (spaces < 0) spaces = 0;
- size_t szs = sizesTotal(id);
+ size_t szs = mSizesTotal[id];
totalSize += szs;
- size_t els = elementsTotal(id);
+ size_t els = mElementsTotal[id];
totalEls += els;
output +=
android::base::StringPrintf("%*s%zu/%zu", spaces, "", szs, els);
@@ -663,11 +786,11 @@
log_id_for_each(id) {
if (!(logMask & (1 << id))) continue;
- size_t els = elements(id);
+ size_t els = mElements[id];
if (els) {
oldLength = output.length();
if (spaces < 0) spaces = 0;
- size_t szs = sizes(id);
+ size_t szs = mSizes[id];
totalSize += szs;
totalEls += els;
output +=
@@ -749,7 +872,7 @@
log_id_for_each(id) {
if (!(logMask & (1 << id))) continue;
- size_t els = elements(id);
+ size_t els = mElements[id];
if (els) {
oldLength = output.length();
if (spaces < 0) spaces = 0;
@@ -758,7 +881,7 @@
((sizeof(LogBufferElement) + sizeof(uint64_t) - 1) &
-sizeof(uint64_t)) +
sizeof(std::list<LogBufferElement*>);
- size_t szs = sizes(id) + els * overhead;
+ size_t szs = mSizes[id] + els * overhead;
totalSize += szs;
output += android::base::StringPrintf("%*s%zu", spaces, "", szs);
spaces -= output.length() - oldLength;
@@ -779,39 +902,38 @@
name = (uid == AID_ROOT) ? "Chattiest UIDs in %s log buffer:"
: "Logging for your UID in %s log buffer:";
- output += uidTable[id].format(*this, uid, pid, name, id);
+ output += FormatTable(uidTable[id], uid, pid, name, id);
}
if (enable) {
name = ((uid == AID_ROOT) && !pid) ? "Chattiest PIDs:"
: "Logging for this PID:";
- output += pidTable.format(*this, uid, pid, name);
+ output += FormatTable(pidTable, uid, pid, name);
name = "Chattiest TIDs";
if (pid) name += android::base::StringPrintf(" for PID %d", pid);
name += ":";
- output += tidTable.format(*this, uid, pid, name);
+ output += FormatTable(tidTable, uid, pid, name);
}
if (enable && (logMask & (1 << LOG_ID_EVENTS))) {
name = "Chattiest events log buffer TAGs";
if (pid) name += android::base::StringPrintf(" for PID %d", pid);
name += ":";
- output += tagTable.format(*this, uid, pid, name, LOG_ID_EVENTS);
+ output += FormatTable(tagTable, uid, pid, name, LOG_ID_EVENTS);
}
if (enable && (logMask & (1 << LOG_ID_SECURITY))) {
name = "Chattiest security log buffer TAGs";
if (pid) name += android::base::StringPrintf(" for PID %d", pid);
name += ":";
- output +=
- securityTagTable.format(*this, uid, pid, name, LOG_ID_SECURITY);
+ output += FormatTable(securityTagTable, uid, pid, name, LOG_ID_SECURITY);
}
if (enable) {
name = "Chattiest TAGs";
if (pid) name += android::base::StringPrintf(" for PID %d", pid);
name += ":";
- output += tagNameTable.format(*this, uid, pid, name);
+ output += FormatTable(tagNameTable, uid, pid, name);
}
return output;
@@ -839,15 +961,17 @@
}
}
-uid_t LogStatistics::pidToUid(pid_t pid) {
- return pidTable.add(pid)->second.getUid();
+uid_t LogStatistics::PidToUid(pid_t pid) {
+ auto lock = std::lock_guard{lock_};
+ return pidTable.Add(pid)->second.uid();
}
// caller must free character string
-const char* LogStatistics::pidToName(pid_t pid) const {
+const char* LogStatistics::PidToName(pid_t pid) const {
+ auto lock = std::lock_guard{lock_};
// An inconvenient truth ... getName() can alter the object
pidTable_t& writablePidTable = const_cast<pidTable_t&>(pidTable);
- const char* name = writablePidTable.add(pid)->second.getName();
+ const char* name = writablePidTable.Add(pid)->second.name();
if (!name) {
return nullptr;
}
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index 0782de3..200c228 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LOGD_LOG_STATISTICS_H__
-#define _LOGD_LOG_STATISTICS_H__
+#pragma once
#include <ctype.h>
#include <inttypes.h>
@@ -25,24 +24,40 @@
#include <sys/types.h>
#include <algorithm> // std::max
+#include <array>
#include <memory>
+#include <mutex>
#include <string>
#include <string_view>
#include <unordered_map>
#include <android-base/stringprintf.h>
+#include <android-base/thread_annotations.h>
#include <android/log.h>
#include <log/log_time.h>
#include <private/android_filesystem_config.h>
#include <utils/FastStrcmp.h>
-#include "LogBufferElement.h"
#include "LogUtils.h"
#define log_id_for_each(i) \
for (log_id_t i = LOG_ID_MIN; (i) < LOG_ID_MAX; (i) = (log_id_t)((i) + 1))
class LogStatistics;
+class UidEntry;
+class PidEntry;
+
+struct LogStatisticsElement {
+ uid_t uid;
+ pid_t pid;
+ pid_t tid;
+ uint32_t tag;
+ log_time realtime;
+ const char* msg;
+ uint16_t msg_len;
+ uint16_t dropped_count;
+ log_id_t log_id;
+};
template <typename TKey, typename TEntry>
class LogHashtable {
@@ -63,7 +78,7 @@
static const size_t unordered_map_per_entry_overhead = sizeof(void*);
static const size_t unordered_map_bucket_overhead = sizeof(void*);
- public:
+ public:
size_t size() const {
return map.size();
}
@@ -79,162 +94,111 @@
typedef
typename std::unordered_map<TKey, TEntry>::const_iterator const_iterator;
- std::unique_ptr<const TEntry* []> sort(uid_t uid, pid_t pid,
- size_t len) const {
- if (!len) {
- std::unique_ptr<const TEntry* []> sorted(nullptr);
- return sorted;
- }
-
- const TEntry** retval = new const TEntry*[len];
- memset(retval, 0, sizeof(*retval) * len);
-
- for (const_iterator it = map.begin(); it != map.end(); ++it) {
- const TEntry& entry = it->second;
-
- if ((uid != AID_ROOT) && (uid != entry.getUid())) {
+ // Returns a sorted array of up to len highest entries sorted by size. If fewer than len
+ // entries are found, their positions are set to nullptr.
+ template <size_t len>
+ void MaxEntries(uid_t uid, pid_t pid, std::array<const TKey*, len>& out_keys,
+ std::array<const TEntry*, len>& out_entries) const {
+ out_keys.fill(nullptr);
+ out_entries.fill(nullptr);
+ for (const auto& [key, entry] : map) {
+ uid_t entry_uid = 0;
+ if constexpr (std::is_same_v<TEntry, UidEntry>) {
+ entry_uid = key;
+ } else {
+ entry_uid = entry.uid();
+ }
+ if (uid != AID_ROOT && uid != entry_uid) {
continue;
}
- if (pid && entry.getPid() && (pid != entry.getPid())) {
+ pid_t entry_pid = 0;
+ if constexpr (std::is_same_v<TEntry, PidEntry>) {
+ entry_pid = key;
+ } else {
+ entry_pid = entry.pid();
+ }
+ if (pid && entry_pid && pid != entry_pid) {
continue;
}
size_t sizes = entry.getSizes();
ssize_t index = len - 1;
- while ((!retval[index] || (sizes > retval[index]->getSizes())) &&
- (--index >= 0))
+ while ((!out_entries[index] || sizes > out_entries[index]->getSizes()) && --index >= 0)
;
if (++index < (ssize_t)len) {
size_t num = len - index - 1;
if (num) {
- memmove(&retval[index + 1], &retval[index],
- num * sizeof(retval[0]));
+ memmove(&out_keys[index + 1], &out_keys[index], num * sizeof(out_keys[0]));
+ memmove(&out_entries[index + 1], &out_entries[index],
+ num * sizeof(out_entries[0]));
}
- retval[index] = &entry;
+ out_keys[index] = &key;
+ out_entries[index] = &entry;
}
}
- std::unique_ptr<const TEntry* []> sorted(retval);
- return sorted;
}
- inline iterator add(const TKey& key, const LogBufferElement* element) {
+ iterator Add(const TKey& key, const LogStatisticsElement& element) {
iterator it = map.find(key);
if (it == map.end()) {
it = map.insert(std::make_pair(key, TEntry(element))).first;
} else {
- it->second.add(element);
+ it->second.Add(element);
}
return it;
}
- inline iterator add(TKey key) {
+ iterator Add(const TKey& key) {
iterator it = map.find(key);
if (it == map.end()) {
it = map.insert(std::make_pair(key, TEntry(key))).first;
} else {
- it->second.add(key);
+ it->second.Add(key);
}
return it;
}
- void subtract(TKey&& key, const LogBufferElement* element) {
- iterator it = map.find(std::move(key));
- if ((it != map.end()) && it->second.subtract(element)) {
- map.erase(it);
- }
- }
-
- void subtract(const TKey& key, const LogBufferElement* element) {
+ void Subtract(const TKey& key, const LogStatisticsElement& element) {
iterator it = map.find(key);
- if ((it != map.end()) && it->second.subtract(element)) {
+ if (it != map.end() && it->second.Subtract(element)) {
map.erase(it);
}
}
- inline void drop(TKey key, const LogBufferElement* element) {
+ void Drop(const TKey& key, const LogStatisticsElement& element) {
iterator it = map.find(key);
if (it != map.end()) {
- it->second.drop(element);
+ it->second.Drop(element);
}
}
- inline iterator begin() {
- return map.begin();
- }
- inline const_iterator begin() const {
- return map.begin();
- }
- inline iterator end() {
- return map.end();
- }
- inline const_iterator end() const {
- return map.end();
- }
-
- std::string format(const LogStatistics& stat, uid_t uid, pid_t pid,
- const std::string& name = std::string(""),
- log_id_t id = LOG_ID_MAX) const {
- static const size_t maximum_sorted_entries = 32;
- std::string output;
- std::unique_ptr<const TEntry* []> sorted =
- sort(uid, pid, maximum_sorted_entries);
- if (!sorted.get()) {
- return output;
- }
- bool headerPrinted = false;
- for (size_t index = 0; index < maximum_sorted_entries; ++index) {
- const TEntry* entry = sorted[index];
- if (!entry) {
- break;
- }
- if (entry->getSizes() <= (sorted[0]->getSizes() / 100)) {
- break;
- }
- if (!headerPrinted) {
- output += "\n\n";
- output += entry->formatHeader(name, id);
- headerPrinted = true;
- }
- output += entry->format(stat, id);
- }
- return output;
- }
+ iterator begin() { return map.begin(); }
+ const_iterator begin() const { return map.begin(); }
+ iterator end() { return map.end(); }
+ const_iterator end() const { return map.end(); }
};
-namespace EntryBaseConstants {
-static constexpr size_t pruned_len = 14;
-static constexpr size_t total_len = 80;
-}
+class EntryBase {
+ public:
+ EntryBase() : size_(0) {}
+ explicit EntryBase(const LogStatisticsElement& element) : size_(element.msg_len) {}
-struct EntryBase {
- size_t size;
+ size_t getSizes() const { return size_; }
- EntryBase() : size(0) {
- }
- explicit EntryBase(const LogBufferElement* element)
- : size(element->getMsgLen()) {
+ void Add(const LogStatisticsElement& element) { size_ += element.msg_len; }
+ bool Subtract(const LogStatisticsElement& element) {
+ size_ -= element.msg_len;
+ return !size_;
}
- size_t getSizes() const {
- return size;
- }
-
- inline void add(const LogBufferElement* element) {
- size += element->getMsgLen();
- }
- inline bool subtract(const LogBufferElement* element) {
- size -= element->getMsgLen();
- return !size;
- }
+ static constexpr size_t PRUNED_LEN = 14;
+ static constexpr size_t TOTAL_LEN = 80;
static std::string formatLine(const std::string& name,
const std::string& size,
const std::string& pruned) {
- ssize_t drop_len =
- std::max(pruned.length() + 1, EntryBaseConstants::pruned_len);
- ssize_t size_len =
- std::max(size.length() + 1, EntryBaseConstants::total_len -
- name.length() - drop_len - 1);
+ ssize_t drop_len = std::max(pruned.length() + 1, PRUNED_LEN);
+ ssize_t size_len = std::max(size.length() + 1, TOTAL_LEN - name.length() - drop_len - 1);
std::string ret = android::base::StringPrintf(
"%s%*s%*s", name.c_str(), (int)size_len, size.c_str(),
@@ -246,545 +210,353 @@
if (len) ret.erase(pos + 1, len);
return ret + "\n";
}
+
+ private:
+ size_t size_;
};
-struct EntryBaseDropped : public EntryBase {
- size_t dropped;
+class EntryBaseDropped : public EntryBase {
+ public:
+ EntryBaseDropped() : dropped_(0) {}
+ explicit EntryBaseDropped(const LogStatisticsElement& element)
+ : EntryBase(element), dropped_(element.dropped_count) {}
- EntryBaseDropped() : dropped(0) {
+ size_t dropped_count() const { return dropped_; }
+
+ void Add(const LogStatisticsElement& element) {
+ dropped_ += element.dropped_count;
+ EntryBase::Add(element);
}
- explicit EntryBaseDropped(const LogBufferElement* element)
- : EntryBase(element), dropped(element->getDropped()) {
+ bool Subtract(const LogStatisticsElement& element) {
+ dropped_ -= element.dropped_count;
+ return EntryBase::Subtract(element) && !dropped_;
+ }
+ void Drop(const LogStatisticsElement& element) {
+ dropped_ += 1;
+ EntryBase::Subtract(element);
}
- size_t getDropped() const {
- return dropped;
- }
-
- inline void add(const LogBufferElement* element) {
- dropped += element->getDropped();
- EntryBase::add(element);
- }
- inline bool subtract(const LogBufferElement* element) {
- dropped -= element->getDropped();
- return EntryBase::subtract(element) && !dropped;
- }
- inline void drop(const LogBufferElement* element) {
- dropped += 1;
- EntryBase::subtract(element);
- }
+ private:
+ size_t dropped_;
};
-struct UidEntry : public EntryBaseDropped {
- const uid_t uid;
- pid_t pid;
+class UidEntry : public EntryBaseDropped {
+ public:
+ explicit UidEntry(const LogStatisticsElement& element)
+ : EntryBaseDropped(element), pid_(element.pid) {}
- explicit UidEntry(const LogBufferElement* element)
- : EntryBaseDropped(element),
- uid(element->getUid()),
- pid(element->getPid()) {
- }
+ pid_t pid() const { return pid_; }
- inline const uid_t& getKey() const {
- return uid;
- }
- inline const uid_t& getUid() const {
- return getKey();
- }
- inline const pid_t& getPid() const {
- return pid;
- }
-
- inline void add(const LogBufferElement* element) {
- if (pid != element->getPid()) {
- pid = -1;
+ void Add(const LogStatisticsElement& element) {
+ if (pid_ != element.pid) {
+ pid_ = -1;
}
- EntryBaseDropped::add(element);
+ EntryBaseDropped::Add(element);
}
std::string formatHeader(const std::string& name, log_id_t id) const;
- std::string format(const LogStatistics& stat, log_id_t id) const;
+ std::string format(const LogStatistics& stat, log_id_t id, uid_t uid) const;
+
+ private:
+ pid_t pid_;
};
namespace android {
uid_t pidToUid(pid_t pid);
}
-struct PidEntry : public EntryBaseDropped {
- const pid_t pid;
- uid_t uid;
- char* name;
-
+class PidEntry : public EntryBaseDropped {
+ public:
explicit PidEntry(pid_t pid)
: EntryBaseDropped(),
- pid(pid),
- uid(android::pidToUid(pid)),
- name(android::pidToName(pid)) {
- }
- explicit PidEntry(const LogBufferElement* element)
- : EntryBaseDropped(element),
- pid(element->getPid()),
- uid(element->getUid()),
- name(android::pidToName(pid)) {
- }
+ uid_(android::pidToUid(pid)),
+ name_(android::pidToName(pid)) {}
+ explicit PidEntry(const LogStatisticsElement& element)
+ : EntryBaseDropped(element), uid_(element.uid), name_(android::pidToName(element.pid)) {}
PidEntry(const PidEntry& element)
: EntryBaseDropped(element),
- pid(element.pid),
- uid(element.uid),
- name(element.name ? strdup(element.name) : nullptr) {
- }
- ~PidEntry() {
- free(name);
- }
+ uid_(element.uid_),
+ name_(element.name_ ? strdup(element.name_) : nullptr) {}
+ ~PidEntry() { free(name_); }
- const pid_t& getKey() const {
- return pid;
- }
- const pid_t& getPid() const {
- return getKey();
- }
- const uid_t& getUid() const {
- return uid;
- }
- const char* getName() const {
- return name;
- }
+ uid_t uid() const { return uid_; }
+ const char* name() const { return name_; }
- inline void add(pid_t newPid) {
- if (name && !fastcmp<strncmp>(name, "zygote", 6)) {
- free(name);
- name = nullptr;
+ void Add(pid_t new_pid) {
+ if (name_ && !fastcmp<strncmp>(name_, "zygote", 6)) {
+ free(name_);
+ name_ = nullptr;
}
- if (!name) {
- name = android::pidToName(newPid);
+ if (!name_) {
+ name_ = android::pidToName(new_pid);
}
}
- inline void add(const LogBufferElement* element) {
- uid_t incomingUid = element->getUid();
- if (getUid() != incomingUid) {
- uid = incomingUid;
- free(name);
- name = android::pidToName(element->getPid());
+ void Add(const LogStatisticsElement& element) {
+ uid_t incoming_uid = element.uid;
+ if (uid() != incoming_uid) {
+ uid_ = incoming_uid;
+ free(name_);
+ name_ = android::pidToName(element.pid);
} else {
- add(element->getPid());
+ Add(element.pid);
}
- EntryBaseDropped::add(element);
+ EntryBaseDropped::Add(element);
}
std::string formatHeader(const std::string& name, log_id_t id) const;
- std::string format(const LogStatistics& stat, log_id_t id) const;
+ std::string format(const LogStatistics& stat, log_id_t id, pid_t pid) const;
+
+ private:
+ uid_t uid_;
+ char* name_;
};
-struct TidEntry : public EntryBaseDropped {
- const pid_t tid;
- pid_t pid;
- uid_t uid;
- char* name;
-
+class TidEntry : public EntryBaseDropped {
+ public:
TidEntry(pid_t tid, pid_t pid)
: EntryBaseDropped(),
- tid(tid),
- pid(pid),
- uid(android::pidToUid(tid)),
- name(android::tidToName(tid)) {
- }
- explicit TidEntry(const LogBufferElement* element)
+ pid_(pid),
+ uid_(android::pidToUid(tid)),
+ name_(android::tidToName(tid)) {}
+ explicit TidEntry(const LogStatisticsElement& element)
: EntryBaseDropped(element),
- tid(element->getTid()),
- pid(element->getPid()),
- uid(element->getUid()),
- name(android::tidToName(tid)) {
- }
+ pid_(element.pid),
+ uid_(element.uid),
+ name_(android::tidToName(element.tid)) {}
TidEntry(const TidEntry& element)
: EntryBaseDropped(element),
- tid(element.tid),
- pid(element.pid),
- uid(element.uid),
- name(element.name ? strdup(element.name) : nullptr) {
- }
- ~TidEntry() {
- free(name);
- }
+ pid_(element.pid_),
+ uid_(element.uid_),
+ name_(element.name_ ? strdup(element.name_) : nullptr) {}
+ ~TidEntry() { free(name_); }
- const pid_t& getKey() const {
- return tid;
- }
- const pid_t& getTid() const {
- return getKey();
- }
- const pid_t& getPid() const {
- return pid;
- }
- const uid_t& getUid() const {
- return uid;
- }
- const char* getName() const {
- return name;
- }
+ pid_t pid() const { return pid_; }
+ uid_t uid() const { return uid_; }
+ const char* name() const { return name_; }
- inline void add(pid_t incomingTid) {
- if (name && !fastcmp<strncmp>(name, "zygote", 6)) {
- free(name);
- name = nullptr;
+ void Add(pid_t incomingTid) {
+ if (name_ && !fastcmp<strncmp>(name_, "zygote", 6)) {
+ free(name_);
+ name_ = nullptr;
}
- if (!name) {
- name = android::tidToName(incomingTid);
+ if (!name_) {
+ name_ = android::tidToName(incomingTid);
}
}
- inline void add(const LogBufferElement* element) {
- uid_t incomingUid = element->getUid();
- pid_t incomingPid = element->getPid();
- if ((getUid() != incomingUid) || (getPid() != incomingPid)) {
- uid = incomingUid;
- pid = incomingPid;
- free(name);
- name = android::tidToName(element->getTid());
+ void Add(const LogStatisticsElement& element) {
+ uid_t incoming_uid = element.uid;
+ pid_t incoming_pid = element.pid;
+ if (uid() != incoming_uid || pid() != incoming_pid) {
+ uid_ = incoming_uid;
+ pid_ = incoming_pid;
+ free(name_);
+ name_ = android::tidToName(element.tid);
} else {
- add(element->getTid());
+ Add(element.tid);
}
- EntryBaseDropped::add(element);
+ EntryBaseDropped::Add(element);
}
std::string formatHeader(const std::string& name, log_id_t id) const;
- std::string format(const LogStatistics& stat, log_id_t id) const;
+ std::string format(const LogStatistics& stat, log_id_t id, pid_t pid) const;
+
+ private:
+ pid_t pid_;
+ uid_t uid_;
+ char* name_;
};
-struct TagEntry : public EntryBaseDropped {
- const uint32_t tag;
- pid_t pid;
- uid_t uid;
+class TagEntry : public EntryBaseDropped {
+ public:
+ explicit TagEntry(const LogStatisticsElement& element)
+ : EntryBaseDropped(element), tag_(element.tag), pid_(element.pid), uid_(element.uid) {}
- explicit TagEntry(const LogBufferElement* element)
- : EntryBaseDropped(element),
- tag(element->getTag()),
- pid(element->getPid()),
- uid(element->getUid()) {
- }
+ uint32_t key() const { return tag_; }
+ pid_t pid() const { return pid_; }
+ uid_t uid() const { return uid_; }
+ const char* name() const { return android::tagToName(tag_); }
- const uint32_t& getKey() const {
- return tag;
- }
- const pid_t& getPid() const {
- return pid;
- }
- const uid_t& getUid() const {
- return uid;
- }
- const char* getName() const {
- return android::tagToName(tag);
- }
-
- inline void add(const LogBufferElement* element) {
- if (uid != element->getUid()) {
- uid = -1;
+ void Add(const LogStatisticsElement& element) {
+ if (uid_ != element.uid) {
+ uid_ = -1;
}
- if (pid != element->getPid()) {
- pid = -1;
+ if (pid_ != element.pid) {
+ pid_ = -1;
}
- EntryBaseDropped::add(element);
+ EntryBaseDropped::Add(element);
}
std::string formatHeader(const std::string& name, log_id_t id) const;
- std::string format(const LogStatistics& stat, log_id_t id) const;
+ std::string format(const LogStatistics& stat, log_id_t id, uint32_t) const;
+
+ private:
+ const uint32_t tag_;
+ pid_t pid_;
+ uid_t uid_;
};
-struct TagNameKey {
- std::string* alloc;
- std::string_view name; // Saves space if const char*
+class TagNameEntry : public EntryBase {
+ public:
+ explicit TagNameEntry(const LogStatisticsElement& element)
+ : EntryBase(element), tid_(element.tid), pid_(element.pid), uid_(element.uid) {}
- explicit TagNameKey(const LogBufferElement* element)
- : alloc(nullptr), name("", strlen("")) {
- if (element->isBinary()) {
- uint32_t tag = element->getTag();
- if (tag) {
- const char* cp = android::tagToName(tag);
- if (cp) {
- name = std::string_view(cp, strlen(cp));
- return;
- }
- }
- alloc = new std::string(
- android::base::StringPrintf("[%" PRIu32 "]", tag));
- if (!alloc) return;
- name = std::string_view(alloc->c_str(), alloc->size());
- return;
+ pid_t tid() const { return tid_; }
+ pid_t pid() const { return pid_; }
+ uid_t uid() const { return uid_; }
+
+ void Add(const LogStatisticsElement& element) {
+ if (uid_ != element.uid) {
+ uid_ = -1;
}
- const char* msg = element->getMsg();
- if (!msg) {
- name = std::string_view("chatty", strlen("chatty"));
- return;
+ if (pid_ != element.pid) {
+ pid_ = -1;
}
- ++msg;
- uint16_t len = element->getMsgLen();
- len = (len <= 1) ? 0 : strnlen(msg, len - 1);
- if (!len) {
- name = std::string_view("<NULL>", strlen("<NULL>"));
- return;
+ if (tid_ != element.tid) {
+ tid_ = -1;
}
- alloc = new std::string(msg, len);
- if (!alloc) return;
- name = std::string_view(alloc->c_str(), alloc->size());
- }
-
- explicit TagNameKey(TagNameKey&& rval) noexcept
- : alloc(rval.alloc), name(rval.name.data(), rval.name.length()) {
- rval.alloc = nullptr;
- }
-
- explicit TagNameKey(const TagNameKey& rval)
- : alloc(rval.alloc ? new std::string(*rval.alloc) : nullptr),
- name(alloc ? alloc->data() : rval.name.data(), rval.name.length()) {
- }
-
- ~TagNameKey() {
- if (alloc) delete alloc;
- }
-
- operator const std::string_view() const {
- return name;
- }
-
- const char* data() const {
- return name.data();
- }
- size_t length() const {
- return name.length();
- }
-
- bool operator==(const TagNameKey& rval) const {
- if (length() != rval.length()) return false;
- if (length() == 0) return true;
- return fastcmp<strncmp>(data(), rval.data(), length()) == 0;
- }
- bool operator!=(const TagNameKey& rval) const {
- return !(*this == rval);
- }
-
- size_t getAllocLength() const {
- return alloc ? alloc->length() + 1 + sizeof(std::string) : 0;
- }
-};
-
-// Hash for TagNameKey
-template <>
-struct std::hash<TagNameKey>
- : public std::unary_function<const TagNameKey&, size_t> {
- size_t operator()(const TagNameKey& __t) const noexcept {
- if (!__t.length()) return 0;
- return std::hash<std::string_view>()(std::string_view(__t));
- }
-};
-
-struct TagNameEntry : public EntryBase {
- pid_t tid;
- pid_t pid;
- uid_t uid;
- TagNameKey name;
-
- explicit TagNameEntry(const LogBufferElement* element)
- : EntryBase(element),
- tid(element->getTid()),
- pid(element->getPid()),
- uid(element->getUid()),
- name(element) {
- }
-
- const TagNameKey& getKey() const {
- return name;
- }
- const pid_t& getTid() const {
- return tid;
- }
- const pid_t& getPid() const {
- return pid;
- }
- const uid_t& getUid() const {
- return uid;
- }
- const char* getName() const {
- return name.data();
- }
- size_t getNameAllocLength() const {
- return name.getAllocLength();
- }
-
- inline void add(const LogBufferElement* element) {
- if (uid != element->getUid()) {
- uid = -1;
- }
- if (pid != element->getPid()) {
- pid = -1;
- }
- if (tid != element->getTid()) {
- tid = -1;
- }
- EntryBase::add(element);
+ EntryBase::Add(element);
}
std::string formatHeader(const std::string& name, log_id_t id) const;
- std::string format(const LogStatistics& stat, log_id_t id) const;
+ std::string format(const LogStatistics& stat, log_id_t id, const std::string& key_name) const;
+
+ private:
+ pid_t tid_;
+ pid_t pid_;
+ uid_t uid_;
};
-template <typename TEntry>
-class LogFindWorst {
- std::unique_ptr<const TEntry* []> sorted;
-
- public:
- explicit LogFindWorst(std::unique_ptr<const TEntry* []>&& sorted)
- : sorted(std::move(sorted)) {
- }
-
- void findWorst(int& worst, size_t& worst_sizes, size_t& second_worst_sizes,
- size_t threshold) {
- if (sorted.get() && sorted[0] && sorted[1]) {
- worst_sizes = sorted[0]->getSizes();
- if ((worst_sizes > threshold)
- // Allow time horizon to extend roughly tenfold, assume
- // average entry length is 100 characters.
- && (worst_sizes > (10 * sorted[0]->getDropped()))) {
- worst = sorted[0]->getKey();
- second_worst_sizes = sorted[1]->getSizes();
- if (second_worst_sizes < threshold) {
- second_worst_sizes = threshold;
- }
- }
- }
- }
-
- void findWorst(int& worst, size_t worst_sizes, size_t& second_worst_sizes) {
- if (sorted.get() && sorted[0] && sorted[1]) {
- worst = sorted[0]->getKey();
- second_worst_sizes =
- worst_sizes - sorted[0]->getSizes() + sorted[1]->getSizes();
- }
- }
-};
-
-// Log Statistics
class LogStatistics {
friend UidEntry;
+ friend PidEntry;
+ friend TidEntry;
- size_t mSizes[LOG_ID_MAX];
- size_t mElements[LOG_ID_MAX];
- size_t mDroppedElements[LOG_ID_MAX];
- size_t mSizesTotal[LOG_ID_MAX];
- size_t mElementsTotal[LOG_ID_MAX];
- log_time mOldest[LOG_ID_MAX];
- log_time mNewest[LOG_ID_MAX];
- log_time mNewestDropped[LOG_ID_MAX];
- static size_t SizesTotal;
+ size_t mSizes[LOG_ID_MAX] GUARDED_BY(lock_);
+ size_t mElements[LOG_ID_MAX] GUARDED_BY(lock_);
+ size_t mDroppedElements[LOG_ID_MAX] GUARDED_BY(lock_);
+ size_t mSizesTotal[LOG_ID_MAX] GUARDED_BY(lock_);
+ size_t mElementsTotal[LOG_ID_MAX] GUARDED_BY(lock_);
+ log_time mOldest[LOG_ID_MAX] GUARDED_BY(lock_);
+ log_time mNewest[LOG_ID_MAX] GUARDED_BY(lock_);
+ log_time mNewestDropped[LOG_ID_MAX] GUARDED_BY(lock_);
+ static std::atomic<size_t> SizesTotal;
bool enable;
// uid to size list
typedef LogHashtable<uid_t, UidEntry> uidTable_t;
- uidTable_t uidTable[LOG_ID_MAX];
+ uidTable_t uidTable[LOG_ID_MAX] GUARDED_BY(lock_);
// pid of system to size list
typedef LogHashtable<pid_t, PidEntry> pidSystemTable_t;
- pidSystemTable_t pidSystemTable[LOG_ID_MAX];
+ pidSystemTable_t pidSystemTable[LOG_ID_MAX] GUARDED_BY(lock_);
// pid to uid list
typedef LogHashtable<pid_t, PidEntry> pidTable_t;
- pidTable_t pidTable;
+ pidTable_t pidTable GUARDED_BY(lock_);
// tid to uid list
typedef LogHashtable<pid_t, TidEntry> tidTable_t;
- tidTable_t tidTable;
+ tidTable_t tidTable GUARDED_BY(lock_);
// tag list
typedef LogHashtable<uint32_t, TagEntry> tagTable_t;
- tagTable_t tagTable;
+ tagTable_t tagTable GUARDED_BY(lock_);
// security tag list
- tagTable_t securityTagTable;
+ tagTable_t securityTagTable GUARDED_BY(lock_);
// global tag list
- typedef LogHashtable<TagNameKey, TagNameEntry> tagNameTable_t;
+ typedef LogHashtable<std::string, TagNameEntry> tagNameTable_t;
tagNameTable_t tagNameTable;
- size_t sizeOf() const {
+ size_t sizeOf() const REQUIRES(lock_) {
size_t size = sizeof(*this) + pidTable.sizeOf() + tidTable.sizeOf() +
tagTable.sizeOf() + securityTagTable.sizeOf() +
tagNameTable.sizeOf() +
(pidTable.size() * sizeof(pidTable_t::iterator)) +
(tagTable.size() * sizeof(tagTable_t::iterator));
for (auto it : pidTable) {
- const char* name = it.second.getName();
+ const char* name = it.second.name();
if (name) size += strlen(name) + 1;
}
for (auto it : tidTable) {
- const char* name = it.second.getName();
+ const char* name = it.second.name();
if (name) size += strlen(name) + 1;
}
- for (auto it : tagNameTable) size += it.second.getNameAllocLength();
+ for (auto it : tagNameTable) {
+ size += sizeof(std::string);
+ size_t len = it.first.size();
+ // Account for short string optimization: if the string's length is <= 22 bytes for 64
+ // bit or <= 10 bytes for 32 bit, then there is no additional allocation.
+ if ((sizeof(std::string) == 24 && len > 22) ||
+ (sizeof(std::string) != 24 && len > 10)) {
+ size += len;
+ }
+ }
log_id_for_each(id) {
size += uidTable[id].sizeOf();
size += uidTable[id].size() * sizeof(uidTable_t::iterator);
size += pidSystemTable[id].sizeOf();
- size +=
- pidSystemTable[id].size() * sizeof(pidSystemTable_t::iterator);
+ size += pidSystemTable[id].size() * sizeof(pidSystemTable_t::iterator);
}
return size;
}
- public:
- LogStatistics();
+ public:
+ LogStatistics(bool enable_statistics);
- void enableStatistics() {
- enable = true;
- }
-
- void addTotal(LogBufferElement* entry);
- void add(LogBufferElement* entry);
- void subtract(LogBufferElement* entry);
+ void AddTotal(log_id_t log_id, uint16_t size) EXCLUDES(lock_);
+ void Add(const LogStatisticsElement& entry) EXCLUDES(lock_);
+ void Subtract(const LogStatisticsElement& entry) EXCLUDES(lock_);
// entry->setDropped(1) must follow this call
- void drop(LogBufferElement* entry);
+ void Drop(const LogStatisticsElement& entry) EXCLUDES(lock_);
// Correct for coalescing two entries referencing dropped content
- void erase(LogBufferElement* element) {
- log_id_t log_id = element->getLogId();
+ void Erase(const LogStatisticsElement& element) EXCLUDES(lock_) {
+ auto lock = std::lock_guard{lock_};
+ log_id_t log_id = element.log_id;
--mElements[log_id];
--mDroppedElements[log_id];
}
- LogFindWorst<UidEntry> sort(uid_t uid, pid_t pid, size_t len, log_id id) {
- return LogFindWorst<UidEntry>(uidTable[id].sort(uid, pid, len));
- }
- LogFindWorst<PidEntry> sortPids(uid_t uid, pid_t pid, size_t len,
- log_id id) {
- return LogFindWorst<PidEntry>(pidSystemTable[id].sort(uid, pid, len));
- }
- LogFindWorst<TagEntry> sortTags(uid_t uid, pid_t pid, size_t len, log_id) {
- return LogFindWorst<TagEntry>(tagTable.sort(uid, pid, len));
- }
+ void WorstTwoUids(log_id id, size_t threshold, int* worst, size_t* worst_sizes,
+ size_t* second_worst_sizes) const EXCLUDES(lock_);
+ void WorstTwoTags(size_t threshold, int* worst, size_t* worst_sizes,
+ size_t* second_worst_sizes) const EXCLUDES(lock_);
+ void WorstTwoSystemPids(log_id id, size_t worst_uid_sizes, int* worst,
+ size_t* second_worst_sizes) const EXCLUDES(lock_);
- // fast track current value by id only
- size_t sizes(log_id_t id) const {
+ bool ShouldPrune(log_id id, unsigned long max_size, unsigned long* prune_rows) const
+ EXCLUDES(lock_);
+
+ // Snapshot of the sizes for a given log buffer.
+ size_t Sizes(log_id_t id) const EXCLUDES(lock_) {
+ auto lock = std::lock_guard{lock_};
return mSizes[id];
}
- size_t elements(log_id_t id) const {
- return mElements[id];
- }
- size_t realElements(log_id_t id) const {
- return mElements[id] - mDroppedElements[id];
- }
- size_t sizesTotal(log_id_t id) const {
- return mSizesTotal[id];
- }
- size_t elementsTotal(log_id_t id) const {
- return mElementsTotal[id];
- }
+ // TODO: Get rid of this entirely.
static size_t sizesTotal() {
return SizesTotal;
}
- std::string format(uid_t uid, pid_t pid, unsigned int logMask) const;
+ std::string Format(uid_t uid, pid_t pid, unsigned int logMask) const EXCLUDES(lock_);
- // helper (must be locked directly or implicitly by mLogElementsLock)
- const char* pidToName(pid_t pid) const;
- uid_t pidToUid(pid_t pid);
- const char* uidToName(uid_t uid) const;
+ const char* PidToName(pid_t pid) const EXCLUDES(lock_);
+ uid_t PidToUid(pid_t pid) EXCLUDES(lock_);
+ const char* UidToName(uid_t uid) const EXCLUDES(lock_);
+
+ private:
+ template <typename TKey, typename TEntry>
+ void WorstTwoWithThreshold(const LogHashtable<TKey, TEntry>& table, size_t threshold,
+ int* worst, size_t* worst_sizes, size_t* second_worst_sizes) const;
+ template <typename TKey, typename TEntry>
+ std::string FormatTable(const LogHashtable<TKey, TEntry>& table, uid_t uid, pid_t pid,
+ const std::string& name = std::string(""),
+ log_id_t id = LOG_ID_MAX) const REQUIRES(lock_);
+ void FormatTmp(const char* nameTmp, uid_t uid, std::string& name, std::string& size,
+ size_t nameLen) const REQUIRES(lock_);
+ const char* UidToNameLocked(uid_t uid) const REQUIRES(lock_);
+
+ mutable std::mutex lock_;
};
-
-#endif // _LOGD_LOG_STATISTICS_H__
diff --git a/logd/LogTags.cpp b/logd/LogTags.cpp
index 0cc7886..8e18f9d 100644
--- a/logd/LogTags.cpp
+++ b/logd/LogTags.cpp
@@ -29,13 +29,17 @@
#include <string>
#include <android-base/file.h>
+#include <android-base/logging.h>
#include <android-base/macros.h>
#include <android-base/scopeguard.h>
#include <android-base/stringprintf.h>
+#include <android-base/threads.h>
#include <log/log_event_list.h>
#include <log/log_properties.h>
+#include <log/log_read.h>
#include <private/android_filesystem_config.h>
+#include "LogStatistics.h"
#include "LogTags.h"
#include "LogUtils.h"
@@ -98,8 +102,7 @@
struct tm tm;
localtime_r(&now, &tm);
char timebuf[20];
- size_t len =
- strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S", &tm);
+ strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S", &tm);
android::base::WriteStringToFd(
android::base::StringPrintf(
"# Rebuilt %.20s, content owned by logd\n", timebuf),
@@ -113,10 +116,11 @@
}
if (warn) {
- android::prdebug(
- ((fd < 0) ? "%s failed to rebuild"
- : "%s missing, damaged or truncated; rebuilt"),
- filename);
+ if (fd < 0) {
+ LOG(ERROR) << filename << " failed to rebuild";
+ } else {
+ LOG(ERROR) << filename << " missing, damaged or truncated; rebuilt";
+ }
}
if (fd >= 0) {
@@ -180,15 +184,13 @@
WritePersistEventLogTags(tag, uid, source);
} else if (warn && !newOne && source) {
// For the files, we want to report dupes.
- android::prdebug("Multiple tag %" PRIu32 " %s %s %s", tag, Name.c_str(),
- Format.c_str(), source);
+ LOG(DEBUG) << "Multiple tag " << tag << " " << Name << " " << Format << " " << source;
}
}
// Read the event log tags file, and build up our internal database
void LogTags::ReadFileEventLogTags(const char* filename, bool warn) {
bool etc = !strcmp(filename, system_event_log_tags);
- bool debug = !etc && !strcmp(filename, debug_event_log_tags);
if (!etc) {
RebuildFileEventLogTags(filename, warn);
@@ -215,7 +217,7 @@
} else if (isdigit(*cp)) {
unsigned long Tag = strtoul(cp, &cp, 10);
if (warn && (Tag > emptyTag)) {
- android::prdebug("tag too large %lu", Tag);
+ LOG(WARNING) << "tag too large " << Tag;
}
while ((cp < endp) && (*cp != '\n') && isspace(*cp)) ++cp;
if (cp >= endp) break;
@@ -230,9 +232,8 @@
std::string Name(name, cp - name);
#ifdef ALLOW_NOISY_LOGGING_OF_PROBLEM_WITH_LOTS_OF_TECHNICAL_DEBT
static const size_t maximum_official_tag_name_size = 24;
- if (warn &&
- (Name.length() > maximum_official_tag_name_size)) {
- android::prdebug("tag name too long %s", Name.c_str());
+ if (warn && (Name.length() > maximum_official_tag_name_size)) {
+ LOG(WARNING) << "tag name too long " << Name;
}
#endif
if (hasAlpha &&
@@ -263,8 +264,8 @@
filename, warn);
} else {
if (warn) {
- android::prdebug("tag name invalid %.*s",
- (int)(cp - name + 1), name);
+ LOG(ERROR) << android::base::StringPrintf("tag name invalid %.*s",
+ (int)(cp - name + 1), name);
}
lineStart = nullptr;
}
@@ -275,7 +276,7 @@
cp++;
}
} else if (warn) {
- android::prdebug("Cannot read %s", filename);
+ LOG(ERROR) << "Cannot read " << filename;
}
}
@@ -289,9 +290,8 @@
// special pmsg event for log tags, and build up our internal
// database with any found.
void LogTags::ReadPersistEventLogTags() {
- struct logger_list* logger_list = android_logger_list_alloc(
- ANDROID_LOG_RDONLY | ANDROID_LOG_PSTORE | ANDROID_LOG_NONBLOCK, 0,
- (pid_t)0);
+ struct logger_list* logger_list =
+ android_logger_list_alloc(ANDROID_LOG_PSTORE | ANDROID_LOG_NONBLOCK, 0, (pid_t)0);
if (!logger_list) return;
struct logger* e = android_logger_open(logger_list, LOG_ID_EVENTS);
@@ -391,23 +391,6 @@
return me->tagToName(tag);
}
-// Prototype in LogUtils.h allowing external access to our database.
-//
-// This only works on userdebug and eng devices to re-read the
-// /data/misc/logd/event-log-tags file right after /data is mounted.
-// The operation is near to boot and should only happen once. There
-// are races associated with its use since it can trigger a Rebuild
-// of the file, but that is a can-not-happen since the file was not
-// read yet. More dangerous if called later, but if all is well it
-// should just skip over everything and not write any new entries.
-void android::ReReadEventLogTags() {
- LogTags* me = logtags;
-
- if (me && __android_log_is_debuggable()) {
- me->ReadFileEventLogTags(me->debug_event_log_tags);
- }
-}
-
// converts an event tag into a format
const char* LogTags::tagToFormat(uint32_t tag) const {
tag2format_const_iterator iform;
@@ -496,8 +479,8 @@
static int openFile(const char* name, int mode, bool warning) {
int fd = TEMP_FAILURE_RETRY(open(name, mode));
- if ((fd < 0) && warning) {
- android::prdebug("Failed open %s (%d)", name, errno);
+ if (fd < 0 && warning) {
+ PLOG(ERROR) << "Failed to open " << name;
}
return fd;
}
@@ -512,7 +495,7 @@
// Every 16K (half the smallest configurable pmsg buffer size) record
static const size_t rate_to_pmsg = 16 * 1024;
- if (lastTotal && ((android::sizesTotal() - lastTotal) < rate_to_pmsg)) {
+ if (lastTotal && (LogStatistics::sizesTotal() - lastTotal) < rate_to_pmsg) {
return;
}
@@ -565,13 +548,13 @@
*/
struct timespec ts;
- clock_gettime(android_log_clockid(), &ts);
+ clock_gettime(CLOCK_REALTIME, &ts);
android_log_header_t header = {
- .id = LOG_ID_EVENTS,
- .tid = (uint16_t)gettid(),
- .realtime.tv_sec = (uint32_t)ts.tv_sec,
- .realtime.tv_nsec = (uint32_t)ts.tv_nsec,
+ .id = LOG_ID_EVENTS,
+ .tid = static_cast<uint16_t>(android::base::GetThreadId()),
+ .realtime.tv_sec = static_cast<uint32_t>(ts.tv_sec),
+ .realtime.tv_nsec = static_cast<uint32_t>(ts.tv_nsec),
};
uint32_t outTag = TAG_DEF_LOG_TAG;
@@ -682,7 +665,7 @@
}
}
- lastTotal = android::sizesTotal();
+ lastTotal = LogStatistics::sizesTotal();
if (!lastTotal) ++lastTotal;
// record totals for next watermark.
diff --git a/logd/LogTags.h b/logd/LogTags.h
index e4d165a..cce700c 100644
--- a/logd/LogTags.h
+++ b/logd/LogTags.h
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-#ifndef _LOGD_LOG_TAGS_H__
-#define _LOGD_LOG_TAGS_H__
+#pragma once
#include <string>
#include <unordered_map>
#include <unordered_set>
+#include <private/android_filesystem_config.h>
#include <utils/RWLock.h>
class LogTags {
@@ -120,5 +120,3 @@
std::string formatGetEventTag(uid_t uid, const char* name,
const char* format);
};
-
-#endif // _LOGD_LOG_TAGS_H__
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp
deleted file mode 100644
index 208d67f..0000000
--- a/logd/LogTimes.cpp
+++ /dev/null
@@ -1,243 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <errno.h>
-#include <string.h>
-#include <sys/prctl.h>
-
-#include <private/android_logger.h>
-
-#include "FlushCommand.h"
-#include "LogBuffer.h"
-#include "LogReader.h"
-#include "LogTimes.h"
-
-pthread_mutex_t LogTimeEntry::timesLock = PTHREAD_MUTEX_INITIALIZER;
-
-LogTimeEntry::LogTimeEntry(LogReader& reader, SocketClient* client,
- bool nonBlock, unsigned long tail, log_mask_t logMask,
- pid_t pid, log_time start, uint64_t timeout)
- : leadingDropped(false),
- mReader(reader),
- mLogMask(logMask),
- mPid(pid),
- mCount(0),
- mTail(tail),
- mIndex(0),
- mClient(client),
- mStart(start),
- mNonBlock(nonBlock),
- mEnd(log_time(android_log_clockid())) {
- mTimeout.tv_sec = timeout / NS_PER_SEC;
- mTimeout.tv_nsec = timeout % NS_PER_SEC;
- memset(mLastTid, 0, sizeof(mLastTid));
- pthread_cond_init(&threadTriggeredCondition, nullptr);
- cleanSkip_Locked();
-}
-
-bool LogTimeEntry::startReader_Locked() {
- pthread_attr_t attr;
-
- if (!pthread_attr_init(&attr)) {
- if (!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
- if (!pthread_create(&mThread, &attr, LogTimeEntry::threadStart,
- this)) {
- pthread_attr_destroy(&attr);
- return true;
- }
- }
- pthread_attr_destroy(&attr);
- }
-
- return false;
-}
-
-void* LogTimeEntry::threadStart(void* obj) {
- prctl(PR_SET_NAME, "logd.reader.per");
-
- LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
-
- SocketClient* client = me->mClient;
-
- LogBuffer& logbuf = me->mReader.logbuf();
-
- bool privileged = FlushCommand::hasReadLogs(client);
- bool security = FlushCommand::hasSecurityLogs(client);
-
- me->leadingDropped = true;
-
- wrlock();
-
- log_time start = me->mStart;
-
- while (!me->mRelease) {
- if (me->mTimeout.tv_sec || me->mTimeout.tv_nsec) {
- if (pthread_cond_timedwait(&me->threadTriggeredCondition,
- ×Lock, &me->mTimeout) == ETIMEDOUT) {
- me->mTimeout.tv_sec = 0;
- me->mTimeout.tv_nsec = 0;
- }
- if (me->mRelease) {
- break;
- }
- }
-
- unlock();
-
- if (me->mTail) {
- logbuf.flushTo(client, start, nullptr, privileged, security,
- FilterFirstPass, me);
- me->leadingDropped = true;
- }
- start = logbuf.flushTo(client, start, me->mLastTid, privileged,
- security, FilterSecondPass, me);
-
- wrlock();
-
- if (start == LogBufferElement::FLUSH_ERROR) {
- break;
- }
-
- me->mStart = start + log_time(0, 1);
-
- if (me->mNonBlock || me->mRelease) {
- break;
- }
-
- me->cleanSkip_Locked();
-
- if (!me->mTimeout.tv_sec && !me->mTimeout.tv_nsec) {
- pthread_cond_wait(&me->threadTriggeredCondition, ×Lock);
- }
- }
-
- LogReader& reader = me->mReader;
- reader.release(client);
-
- client->decRef();
-
- LastLogTimes& times = reader.logbuf().mTimes;
- auto it =
- std::find_if(times.begin(), times.end(),
- [&me](const auto& other) { return other.get() == me; });
-
- if (it != times.end()) {
- times.erase(it);
- }
-
- unlock();
-
- return nullptr;
-}
-
-// A first pass to count the number of elements
-int LogTimeEntry::FilterFirstPass(const LogBufferElement* element, void* obj) {
- LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
-
- LogTimeEntry::wrlock();
-
- if (me->leadingDropped) {
- if (element->getDropped()) {
- LogTimeEntry::unlock();
- return false;
- }
- me->leadingDropped = false;
- }
-
- if (me->mCount == 0) {
- me->mStart = element->getRealTime();
- }
-
- if ((!me->mPid || (me->mPid == element->getPid())) &&
- (me->isWatching(element->getLogId()))) {
- ++me->mCount;
- }
-
- LogTimeEntry::unlock();
-
- return false;
-}
-
-// A second pass to send the selected elements
-int LogTimeEntry::FilterSecondPass(const LogBufferElement* element, void* obj) {
- LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
-
- LogTimeEntry::wrlock();
-
- me->mStart = element->getRealTime();
-
- if (me->skipAhead[element->getLogId()]) {
- me->skipAhead[element->getLogId()]--;
- goto skip;
- }
-
- if (me->leadingDropped) {
- if (element->getDropped()) {
- goto skip;
- }
- me->leadingDropped = false;
- }
-
- // Truncate to close race between first and second pass
- if (me->mNonBlock && me->mTail && (me->mIndex >= me->mCount)) {
- goto stop;
- }
-
- if (!me->isWatching(element->getLogId())) {
- goto skip;
- }
-
- if (me->mPid && (me->mPid != element->getPid())) {
- goto skip;
- }
-
- if (me->mRelease) {
- goto stop;
- }
-
- if (!me->mTail) {
- goto ok;
- }
-
- ++me->mIndex;
-
- if ((me->mCount > me->mTail) && (me->mIndex <= (me->mCount - me->mTail))) {
- goto skip;
- }
-
- if (!me->mNonBlock) {
- me->mTail = 0;
- }
-
-ok:
- if (!me->skipAhead[element->getLogId()]) {
- LogTimeEntry::unlock();
- return true;
- }
-// FALLTHRU
-
-skip:
- LogTimeEntry::unlock();
- return false;
-
-stop:
- LogTimeEntry::unlock();
- return -1;
-}
-
-void LogTimeEntry::cleanSkip_Locked(void) {
- memset(skipAhead, 0, sizeof(skipAhead));
-}
diff --git a/logd/LogTimes.h b/logd/LogTimes.h
deleted file mode 100644
index 9f6f203..0000000
--- a/logd/LogTimes.h
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (C) 2012-2013 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 _LOGD_LOG_TIMES_H__
-#define _LOGD_LOG_TIMES_H__
-
-#include <pthread.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <time.h>
-
-#include <list>
-#include <memory>
-
-#include <log/log.h>
-#include <sysutils/SocketClient.h>
-
-typedef unsigned int log_mask_t;
-
-class LogReader;
-class LogBufferElement;
-
-class LogTimeEntry {
- static pthread_mutex_t timesLock;
- bool mRelease = false;
- bool leadingDropped;
- pthread_cond_t threadTriggeredCondition;
- pthread_t mThread;
- LogReader& mReader;
- static void* threadStart(void* me);
- const log_mask_t mLogMask;
- const pid_t mPid;
- unsigned int skipAhead[LOG_ID_MAX];
- pid_t mLastTid[LOG_ID_MAX];
- unsigned long mCount;
- unsigned long mTail;
- unsigned long mIndex;
-
- public:
- LogTimeEntry(LogReader& reader, SocketClient* client, bool nonBlock,
- unsigned long tail, log_mask_t logMask, pid_t pid,
- log_time start, uint64_t timeout);
-
- SocketClient* mClient;
- log_time mStart;
- struct timespec mTimeout;
- const bool mNonBlock;
- const log_time mEnd; // only relevant if mNonBlock
-
- // Protect List manipulations
- static void wrlock(void) {
- pthread_mutex_lock(×Lock);
- }
- static void rdlock(void) {
- pthread_mutex_lock(×Lock);
- }
- static void unlock(void) {
- pthread_mutex_unlock(×Lock);
- }
-
- bool startReader_Locked();
-
- void triggerReader_Locked(void) {
- pthread_cond_signal(&threadTriggeredCondition);
- }
-
- void triggerSkip_Locked(log_id_t id, unsigned int skip) {
- skipAhead[id] = skip;
- }
- void cleanSkip_Locked(void);
-
- void release_Locked(void) {
- // gracefully shut down the socket.
- shutdown(mClient->getSocket(), SHUT_RDWR);
- mRelease = true;
- pthread_cond_signal(&threadTriggeredCondition);
- }
-
- bool isWatching(log_id_t id) const {
- return mLogMask & (1 << id);
- }
- bool isWatchingMultiple(log_mask_t logMask) const {
- return mLogMask & logMask;
- }
- // flushTo filter callbacks
- static int FilterFirstPass(const LogBufferElement* element, void* me);
- static int FilterSecondPass(const LogBufferElement* element, void* me);
-};
-
-typedef std::list<std::unique_ptr<LogTimeEntry>> LastLogTimes;
-
-#endif // _LOGD_LOG_TIMES_H__
diff --git a/logd/LogUtils.h b/logd/LogUtils.h
index fa9f398..df78a50 100644
--- a/logd/LogUtils.h
+++ b/logd/LogUtils.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LOGD_LOG_UTILS_H__
-#define _LOGD_LOG_UTILS_H__
+#pragma once
#include <sys/cdefs.h>
#include <sys/types.h>
@@ -31,17 +30,13 @@
// Furnished in main.cpp. Caller must own and free returned value
char* uidToName(uid_t uid);
-void prdebug(const char* fmt, ...) __printflike(1, 2);
-// Furnished in LogStatistics.cpp.
-size_t sizesTotal();
// Caller must own and free returned value
char* pidToName(pid_t pid);
char* tidToName(pid_t tid);
// Furnished in LogTags.cpp. Thread safe.
const char* tagToName(uint32_t tag);
-void ReReadEventLogTags();
// Furnished by LogKlog.cpp
char* log_strntok_r(char* s, ssize_t& len, char*& saveptr, ssize_t& sublen);
@@ -64,13 +59,21 @@
}
}
-// Furnished in LogCommand.cpp
-bool clientHasLogCredentials(uid_t uid, gid_t gid, pid_t pid);
-bool clientHasLogCredentials(SocketClient* cli);
+// Returns true if the log buffer is meant for binary logs.
+static inline bool IsBinary(log_id_t log_id) {
+ return log_id == LOG_ID_EVENTS || log_id == LOG_ID_STATS || log_id == LOG_ID_SECURITY;
+}
+
+// Returns the numeric log tag for binary log messages.
+static inline uint32_t MsgToTag(const char* msg, uint16_t msg_len) {
+ if (msg_len < sizeof(android_event_header_t)) {
+ return 0;
+ }
+
+ return reinterpret_cast<const android_event_header_t*>(msg)->tag;
+}
static inline bool worstUidEnabledForLogid(log_id_t id) {
return (id == LOG_ID_MAIN) || (id == LOG_ID_SYSTEM) ||
(id == LOG_ID_RADIO) || (id == LOG_ID_EVENTS);
}
-
-#endif // _LOGD_LOG_UTILS_H__
diff --git a/logd/LogWhiteBlackList.cpp b/logd/LogWhiteBlackList.cpp
index 9d762dc..88a3bdc 100644
--- a/logd/LogWhiteBlackList.cpp
+++ b/logd/LogWhiteBlackList.cpp
@@ -16,13 +16,11 @@
#include <ctype.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
-#include <cutils/properties.h>
#include "LogWhiteBlackList.h"
-// White and Black list
-
Prune::Prune(uid_t uid, pid_t pid) : mUid(uid), mPid(pid) {
}
@@ -75,14 +73,12 @@
it = mNaughty.erase(it);
}
- static const char _default[] = "default";
// default here means take ro.logd.filter, persist.logd.filter then
// internal default in that order.
- if (str && !strcmp(str, _default)) {
+ if (str && !strcmp(str, "default")) {
str = nullptr;
}
- static const char _disable[] = "disable";
- if (str && !strcmp(str, _disable)) {
+ if (str && !strcmp(str, "disable")) {
str = "";
}
@@ -91,22 +87,20 @@
if (str) {
filter = str;
} else {
- char property[PROPERTY_VALUE_MAX];
- property_get("ro.logd.filter", property, _default);
- filter = property;
- property_get("persist.logd.filter", property, filter.c_str());
+ filter = android::base::GetProperty("ro.logd.filter", "default");
+ auto persist_filter = android::base::GetProperty("persist.logd.filter", "default");
// default here means take ro.logd.filter
- if (strcmp(property, _default)) {
- filter = property;
+ if (persist_filter != "default") {
+ filter = persist_filter;
}
}
// default here means take internal default.
- if (filter == _default) {
+ if (filter == "default") {
// See README.property for description of filter format
filter = "~! ~1000/!";
}
- if (filter == _disable) {
+ if (filter == "disable") {
filter = "";
}
diff --git a/logd/LogWhiteBlackList.h b/logd/LogWhiteBlackList.h
index 6e9893b..447ab87 100644
--- a/logd/LogWhiteBlackList.h
+++ b/logd/LogWhiteBlackList.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LOGD_LOG_WHITE_BLACK_LIST_H__
-#define _LOGD_LOG_WHITE_BLACK_LIST_H__
+#pragma once
#include <sys/types.h>
@@ -24,8 +23,6 @@
#include "LogBufferElement.h"
-// White and Blacklist
-
class Prune {
friend class PruneList;
@@ -46,9 +43,7 @@
return mPid;
}
- int cmp(LogBufferElement* e) const {
- return cmp(e->getUid(), e->getPid());
- }
+ int cmp(LogBufferElement* e) const { return cmp(e->uid(), e->pid()); }
std::string format();
};
@@ -84,5 +79,3 @@
std::string format();
};
-
-#endif // _LOGD_LOG_WHITE_BLACK_LIST_H__
diff --git a/logd/LogWriter.h b/logd/LogWriter.h
new file mode 100644
index 0000000..d43c604
--- /dev/null
+++ b/logd/LogWriter.h
@@ -0,0 +1,43 @@
+/*
+ * 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 <string>
+
+#include <log/log_read.h>
+
+// An interface for writing logs to a reader.
+class LogWriter {
+ public:
+ LogWriter(uid_t uid, bool privileged) : uid_(uid), privileged_(privileged) {}
+ virtual ~LogWriter() {}
+
+ virtual bool Write(const logger_entry& entry, const char* msg) = 0;
+ virtual void Shutdown() {}
+ virtual void Release() {}
+
+ virtual std::string name() const = 0;
+ uid_t uid() const { return uid_; }
+
+ bool privileged() const { return privileged_; }
+
+ private:
+ uid_t uid_;
+
+ // If this writer sees logs from all UIDs or only its own UID. See clientHasLogCredentials().
+ bool privileged_;
+};
\ No newline at end of file
diff --git a/logd/README.property b/logd/README.property
index 1b7e165..6a9369a 100644
--- a/logd/README.property
+++ b/logd/README.property
@@ -44,10 +44,6 @@
oldest entries of chattiest UID, and
the chattiest PID of system
(1000, or AID_SYSTEM).
-persist.logd.timestamp string ro The recording timestamp source.
- "m[onotonic]" is the only supported
- key character, otherwise realtime.
-ro.logd.timestamp string realtime default for persist.logd.timestamp
log.tag string persist The global logging level, VERBOSE,
DEBUG, INFO, WARN, ERROR, ASSERT or
SILENT. Only the first character is
diff --git a/logd/SimpleLogBuffer.cpp b/logd/SimpleLogBuffer.cpp
new file mode 100644
index 0000000..292a7e4
--- /dev/null
+++ b/logd/SimpleLogBuffer.cpp
@@ -0,0 +1,367 @@
+/*
+ * 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 "SimpleLogBuffer.h"
+
+#include <android-base/logging.h>
+
+#include "LogBufferElement.h"
+
+SimpleLogBuffer::SimpleLogBuffer(LogReaderList* reader_list, LogTags* tags, LogStatistics* stats)
+ : reader_list_(reader_list), tags_(tags), stats_(stats) {
+ Init();
+}
+
+SimpleLogBuffer::~SimpleLogBuffer() {}
+
+void SimpleLogBuffer::Init() {
+ log_id_for_each(i) {
+ if (SetSize(i, __android_logger_get_buffer_size(i))) {
+ SetSize(i, LOG_BUFFER_MIN_SIZE);
+ }
+ }
+
+ // Release any sleeping reader threads to dump their current content.
+ auto reader_threads_lock = std::lock_guard{reader_list_->reader_threads_lock()};
+ for (const auto& reader_thread : reader_list_->reader_threads()) {
+ reader_thread->triggerReader_Locked();
+ }
+}
+
+std::list<LogBufferElement>::iterator SimpleLogBuffer::GetOldest(log_id_t log_id) {
+ auto it = logs().begin();
+ if (oldest_[log_id]) {
+ it = *oldest_[log_id];
+ }
+ while (it != logs().end() && it->log_id() != log_id) {
+ it++;
+ }
+ if (it != logs().end()) {
+ oldest_[log_id] = it;
+ }
+ return it;
+}
+
+bool SimpleLogBuffer::ShouldLog(log_id_t log_id, const char* msg, uint16_t len) {
+ if (log_id == LOG_ID_SECURITY) {
+ return true;
+ }
+
+ int prio = ANDROID_LOG_INFO;
+ const char* tag = nullptr;
+ size_t tag_len = 0;
+ if (IsBinary(log_id)) {
+ int32_t numeric_tag = MsgToTag(msg, len);
+ tag = tags_->tagToName(numeric_tag);
+ if (tag) {
+ tag_len = strlen(tag);
+ }
+ } else {
+ prio = *msg;
+ tag = msg + 1;
+ tag_len = strnlen(tag, len - 1);
+ }
+ return __android_log_is_loggable_len(prio, tag, tag_len, ANDROID_LOG_VERBOSE);
+}
+
+int SimpleLogBuffer::Log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid,
+ const char* msg, uint16_t len) {
+ if (log_id >= LOG_ID_MAX) {
+ return -EINVAL;
+ }
+
+ if (!ShouldLog(log_id, msg, len)) {
+ // Log traffic received to total
+ stats_->AddTotal(log_id, len);
+ return -EACCES;
+ }
+
+ // Slip the time by 1 nsec if the incoming lands on xxxxxx000 ns.
+ // This prevents any chance that an outside source can request an
+ // exact entry with time specified in ms or us precision.
+ if ((realtime.tv_nsec % 1000) == 0) ++realtime.tv_nsec;
+
+ auto lock = std::lock_guard{lock_};
+ auto sequence = sequence_.fetch_add(1, std::memory_order_relaxed);
+ LogInternal(LogBufferElement(log_id, realtime, uid, pid, tid, sequence, msg, len));
+ return len;
+}
+
+void SimpleLogBuffer::LogInternal(LogBufferElement&& elem) {
+ log_id_t log_id = elem.log_id();
+
+ logs_.emplace_back(std::move(elem));
+ stats_->Add(logs_.back().ToLogStatisticsElement());
+ MaybePrune(log_id);
+ reader_list_->NotifyNewLog(1 << log_id);
+}
+
+// These extra parameters are only required for chatty, but since they're a no-op for
+// SimpleLogBuffer, it's easier to include them here, then to duplicate FlushTo() for
+// ChattyLogBuffer.
+class ChattyFlushToState : public FlushToState {
+ public:
+ ChattyFlushToState(uint64_t start, LogMask log_mask) : FlushToState(start, log_mask) {}
+
+ pid_t* last_tid() { return last_tid_; }
+
+ bool drop_chatty_messages() const { return drop_chatty_messages_; }
+ void set_drop_chatty_messages(bool value) { drop_chatty_messages_ = value; }
+
+ private:
+ pid_t last_tid_[LOG_ID_MAX] = {};
+ bool drop_chatty_messages_ = true;
+};
+
+std::unique_ptr<FlushToState> SimpleLogBuffer::CreateFlushToState(uint64_t start,
+ LogMask log_mask) {
+ return std::make_unique<ChattyFlushToState>(start, log_mask);
+}
+
+bool SimpleLogBuffer::FlushTo(
+ LogWriter* writer, FlushToState& abstract_state,
+ const std::function<FilterResult(log_id_t log_id, pid_t pid, uint64_t sequence,
+ log_time realtime)>& filter) {
+ auto shared_lock = SharedLock{lock_};
+
+ auto& state = reinterpret_cast<ChattyFlushToState&>(abstract_state);
+
+ std::list<LogBufferElement>::iterator it;
+ if (state.start() <= 1) {
+ // client wants to start from the beginning
+ it = logs_.begin();
+ } else {
+ // Client wants to start from some specified time. Chances are
+ // we are better off starting from the end of the time sorted list.
+ for (it = logs_.end(); it != logs_.begin();
+ /* do nothing */) {
+ --it;
+ if (it->sequence() == state.start()) {
+ break;
+ } else if (it->sequence() < state.start()) {
+ it++;
+ break;
+ }
+ }
+ }
+
+ for (; it != logs_.end(); ++it) {
+ LogBufferElement& element = *it;
+
+ state.set_start(element.sequence());
+
+ if (!writer->privileged() && element.uid() != writer->uid()) {
+ continue;
+ }
+
+ if (((1 << element.log_id()) & state.log_mask()) == 0) {
+ continue;
+ }
+
+ if (filter) {
+ FilterResult ret =
+ filter(element.log_id(), element.pid(), element.sequence(), element.realtime());
+ if (ret == FilterResult::kSkip) {
+ continue;
+ }
+ if (ret == FilterResult::kStop) {
+ break;
+ }
+ }
+
+ // drop_chatty_messages is initialized to true, so if the first message that we attempt to
+ // flush is a chatty message, we drop it. Once we see a non-chatty message it gets set to
+ // false to let further chatty messages be printed.
+ if (state.drop_chatty_messages()) {
+ if (element.dropped_count() != 0) {
+ continue;
+ }
+ state.set_drop_chatty_messages(false);
+ }
+
+ bool same_tid = state.last_tid()[element.log_id()] == element.tid();
+ // Dropped (chatty) immediately following a valid log from the same source in the same log
+ // buffer indicates we have a multiple identical squash. chatty that differs source is due
+ // to spam filter. chatty to chatty of different source is also due to spam filter.
+ state.last_tid()[element.log_id()] =
+ (element.dropped_count() && !same_tid) ? 0 : element.tid();
+
+ shared_lock.unlock();
+ // We never prune logs equal to or newer than any LogReaderThreads' `start` value, so the
+ // `element` pointer is safe here without the lock
+ if (!element.FlushTo(writer, stats_, same_tid)) {
+ return false;
+ }
+ shared_lock.lock_shared();
+ }
+
+ state.set_start(state.start() + 1);
+ return true;
+}
+
+// clear all rows of type "id" from the buffer.
+bool SimpleLogBuffer::Clear(log_id_t id, uid_t uid) {
+ bool busy = true;
+ // If it takes more than 4 tries (seconds) to clear, then kill reader(s)
+ for (int retry = 4;;) {
+ if (retry == 1) { // last pass
+ // Check if it is still busy after the sleep, we say prune
+ // one entry, not another clear run, so we are looking for
+ // the quick side effect of the return value to tell us if
+ // we have a _blocked_ reader.
+ {
+ auto lock = std::lock_guard{lock_};
+ busy = Prune(id, 1, uid);
+ }
+ // It is still busy, blocked reader(s), lets kill them all!
+ // otherwise, lets be a good citizen and preserve the slow
+ // readers and let the clear run (below) deal with determining
+ // if we are still blocked and return an error code to caller.
+ if (busy) {
+ auto reader_threads_lock = std::lock_guard{reader_list_->reader_threads_lock()};
+ for (const auto& reader_thread : reader_list_->reader_threads()) {
+ if (reader_thread->IsWatching(id)) {
+ LOG(WARNING) << "Kicking blocked reader, " << reader_thread->name()
+ << ", from LogBuffer::clear()";
+ reader_thread->release_Locked();
+ }
+ }
+ }
+ }
+ {
+ auto lock = std::lock_guard{lock_};
+ busy = Prune(id, ULONG_MAX, uid);
+ }
+
+ if (!busy || !--retry) {
+ break;
+ }
+ sleep(1); // Let reader(s) catch up after notification
+ }
+ return busy;
+}
+
+// get the total space allocated to "id"
+unsigned long SimpleLogBuffer::GetSize(log_id_t id) {
+ auto lock = SharedLock{lock_};
+ size_t retval = max_size_[id];
+ return retval;
+}
+
+// set the total space allocated to "id"
+int SimpleLogBuffer::SetSize(log_id_t id, unsigned long size) {
+ // Reasonable limits ...
+ if (!__android_logger_valid_buffer_size(size)) {
+ return -1;
+ }
+
+ auto lock = std::lock_guard{lock_};
+ max_size_[id] = size;
+ return 0;
+}
+
+void SimpleLogBuffer::MaybePrune(log_id_t id) {
+ unsigned long prune_rows;
+ if (stats_->ShouldPrune(id, max_size_[id], &prune_rows)) {
+ Prune(id, prune_rows, 0);
+ }
+}
+
+bool SimpleLogBuffer::Prune(log_id_t id, unsigned long prune_rows, uid_t caller_uid) {
+ auto reader_threads_lock = std::lock_guard{reader_list_->reader_threads_lock()};
+
+ // Don't prune logs that are newer than the point at which any reader threads are reading from.
+ LogReaderThread* oldest = nullptr;
+ for (const auto& reader_thread : reader_list_->reader_threads()) {
+ if (!reader_thread->IsWatching(id)) {
+ continue;
+ }
+ if (!oldest || oldest->start() > reader_thread->start() ||
+ (oldest->start() == reader_thread->start() &&
+ reader_thread->deadline().time_since_epoch().count() != 0)) {
+ oldest = reader_thread.get();
+ }
+ }
+
+ auto it = GetOldest(id);
+
+ while (it != logs_.end()) {
+ LogBufferElement& element = *it;
+
+ if (element.log_id() != id) {
+ ++it;
+ continue;
+ }
+
+ if (caller_uid != 0 && element.uid() != caller_uid) {
+ ++it;
+ continue;
+ }
+
+ if (oldest && oldest->start() <= element.sequence()) {
+ KickReader(oldest, id, prune_rows);
+ return true;
+ }
+
+ stats_->Subtract(element.ToLogStatisticsElement());
+ it = Erase(it);
+ if (--prune_rows == 0) {
+ return false;
+ }
+ }
+ return false;
+}
+
+std::list<LogBufferElement>::iterator SimpleLogBuffer::Erase(
+ std::list<LogBufferElement>::iterator it) {
+ bool oldest_is_it[LOG_ID_MAX];
+ log_id_for_each(i) { oldest_is_it[i] = oldest_[i] && it == *oldest_[i]; }
+
+ it = logs_.erase(it);
+
+ log_id_for_each(i) {
+ if (oldest_is_it[i]) {
+ if (__predict_false(it == logs().end())) {
+ oldest_[i] = std::nullopt;
+ } else {
+ oldest_[i] = it; // Store the next iterator even if it does not correspond to
+ // the same log_id, as a starting point for GetOldest().
+ }
+ }
+ }
+
+ return it;
+}
+
+// If the selected reader is blocking our pruning progress, decide on
+// what kind of mitigation is necessary to unblock the situation.
+void SimpleLogBuffer::KickReader(LogReaderThread* reader, log_id_t id, unsigned long prune_rows) {
+ if (stats_->Sizes(id) > (2 * max_size_[id])) { // +100%
+ // A misbehaving or slow reader has its connection
+ // dropped if we hit too much memory pressure.
+ LOG(WARNING) << "Kicking blocked reader, " << reader->name()
+ << ", from LogBuffer::kickMe()";
+ reader->release_Locked();
+ } else if (reader->deadline().time_since_epoch().count() != 0) {
+ // Allow a blocked WRAP deadline reader to trigger and start reporting the log data.
+ reader->triggerReader_Locked();
+ } else {
+ // tell slow reader to skip entries to catch up
+ LOG(WARNING) << "Skipping " << prune_rows << " entries from slow reader, " << reader->name()
+ << ", from LogBuffer::kickMe()";
+ reader->triggerSkip_Locked(id, prune_rows);
+ }
+}
diff --git a/logd/SimpleLogBuffer.h b/logd/SimpleLogBuffer.h
new file mode 100644
index 0000000..2172507
--- /dev/null
+++ b/logd/SimpleLogBuffer.h
@@ -0,0 +1,83 @@
+/*
+ * 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 <atomic>
+#include <list>
+#include <mutex>
+
+#include "LogBuffer.h"
+#include "LogBufferElement.h"
+#include "LogReaderList.h"
+#include "LogStatistics.h"
+#include "LogTags.h"
+#include "rwlock.h"
+
+class SimpleLogBuffer : public LogBuffer {
+ public:
+ SimpleLogBuffer(LogReaderList* reader_list, LogTags* tags, LogStatistics* stats);
+ ~SimpleLogBuffer();
+ void Init() override;
+
+ int Log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid, const char* msg,
+ uint16_t len) override;
+ std::unique_ptr<FlushToState> CreateFlushToState(uint64_t start, LogMask log_mask) override;
+ bool FlushTo(LogWriter* writer, FlushToState& state,
+ const std::function<FilterResult(log_id_t log_id, pid_t pid, uint64_t sequence,
+ log_time realtime)>& filter) override;
+
+ bool Clear(log_id_t id, uid_t uid) override;
+ unsigned long GetSize(log_id_t id) override;
+ int SetSize(log_id_t id, unsigned long size) override;
+
+ uint64_t sequence() const override { return sequence_.load(std::memory_order_relaxed); }
+
+ protected:
+ virtual bool Prune(log_id_t id, unsigned long prune_rows, uid_t uid) REQUIRES(lock_);
+ virtual void LogInternal(LogBufferElement&& elem) REQUIRES(lock_);
+
+ // Returns an iterator to the oldest element for a given log type, or logs_.end() if
+ // there are no logs for the given log type. Requires logs_lock_ to be held.
+ std::list<LogBufferElement>::iterator GetOldest(log_id_t log_id) REQUIRES(lock_);
+ std::list<LogBufferElement>::iterator Erase(std::list<LogBufferElement>::iterator it)
+ REQUIRES(lock_);
+ void KickReader(LogReaderThread* reader, log_id_t id, unsigned long prune_rows)
+ REQUIRES_SHARED(lock_);
+
+ LogStatistics* stats() { return stats_; }
+ LogReaderList* reader_list() { return reader_list_; }
+ unsigned long max_size(log_id_t id) REQUIRES_SHARED(lock_) { return max_size_[id]; }
+ std::list<LogBufferElement>& logs() { return logs_; }
+
+ RwLock lock_;
+
+ private:
+ bool ShouldLog(log_id_t log_id, const char* msg, uint16_t len);
+ void MaybePrune(log_id_t id) REQUIRES(lock_);
+
+ LogReaderList* reader_list_;
+ LogTags* tags_;
+ LogStatistics* stats_;
+
+ std::atomic<uint64_t> sequence_ = 1;
+
+ unsigned long max_size_[LOG_ID_MAX] GUARDED_BY(lock_);
+ std::list<LogBufferElement> logs_ GUARDED_BY(lock_);
+ // Keeps track of the iterator to the oldest log message of a given log type, as an
+ // optimization when pruning logs. Use GetOldest() to retrieve.
+ std::optional<std::list<LogBufferElement>::iterator> oldest_[LOG_ID_MAX] GUARDED_BY(lock_);
+};
diff --git a/logd/fuzz/Android.bp b/logd/fuzz/Android.bp
index 299242d..f65fbdf 100644
--- a/logd/fuzz/Android.bp
+++ b/logd/fuzz/Android.bp
@@ -25,6 +25,7 @@
"liblog",
"liblogd",
"libcutils",
+ "libsysutils",
],
cflags: ["-Werror"],
}
diff --git a/logd/fuzz/log_buffer_log_fuzzer.cpp b/logd/fuzz/log_buffer_log_fuzzer.cpp
index 4d1589b..b576ddf 100644
--- a/logd/fuzz/log_buffer_log_fuzzer.cpp
+++ b/logd/fuzz/log_buffer_log_fuzzer.cpp
@@ -15,8 +15,10 @@
*/
#include <string>
-#include "../LogBuffer.h"
-#include "../LogTimes.h"
+#include "../ChattyLogBuffer.h"
+#include "../LogReaderList.h"
+#include "../LogReaderThread.h"
+#include "../LogStatistics.h"
// We don't want to waste a lot of entropy on messages
#define MAX_MSG_LENGTH 5
@@ -36,7 +38,8 @@
unsigned int log_mask;
};
-int write_log_messages(const uint8_t** pdata, size_t* data_left, LogBuffer* log_buffer) {
+int write_log_messages(const uint8_t** pdata, size_t* data_left, LogBuffer* log_buffer,
+ LogStatistics* stats) {
const uint8_t* data = *pdata;
const LogInput* logInput = reinterpret_cast<const LogInput*>(data);
data += sizeof(LogInput);
@@ -69,20 +72,13 @@
// Other elements not in enum.
log_id_t log_id = static_cast<log_id_t>(unsigned(logInput->log_id) % (LOG_ID_MAX + 1));
- log_buffer->log(log_id, logInput->realtime, logInput->uid, logInput->pid, logInput->tid, msg,
+ log_buffer->Log(log_id, logInput->realtime, logInput->uid, logInput->pid, logInput->tid, msg,
sizeof(uint32_t) + msg_length + 1);
- log_buffer->formatStatistics(logInput->uid, logInput->pid, logInput->log_mask);
+ stats->Format(logInput->uid, logInput->pid, logInput->log_mask);
*pdata = data;
return 1;
}
-// Because system/core/logd/main.cpp redefines these.
-void prdebug(char const* fmt, ...) {
- va_list ap;
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
-}
char* uidToName(uid_t) {
return strdup("fake");
}
@@ -93,23 +89,25 @@
return 0;
}
- LastLogTimes times;
- LogBuffer log_buffer(×);
+ LogReaderList reader_list;
+ LogTags tags;
+ PruneList prune_list;
+ LogStatistics stats(true);
+ LogBuffer* log_buffer = new ChattyLogBuffer(&reader_list, &tags, &prune_list, &stats);
size_t data_left = size;
const uint8_t** pdata = &data;
- log_buffer.enableStatistics();
- log_buffer.initPrune(nullptr);
+ prune_list.init(nullptr);
// We want to get pruning code to get called.
- log_id_for_each(i) { log_buffer.setSize(i, 10000); }
+ log_id_for_each(i) { log_buffer->SetSize(i, 10000); }
while (data_left >= sizeof(LogInput) + 2 * sizeof(uint8_t)) {
- if (!write_log_messages(pdata, &data_left, &log_buffer)) {
+ if (!write_log_messages(pdata, &data_left, log_buffer, &stats)) {
return 0;
}
}
- log_id_for_each(i) { log_buffer.clear(i); }
+ log_id_for_each(i) { log_buffer->Clear(i, 0); }
return 0;
}
} // namespace android
diff --git a/logd/libaudit.c b/logd/libaudit.cpp
similarity index 67%
rename from logd/libaudit.c
rename to logd/libaudit.cpp
index f452c71..ccea0a2 100644
--- a/logd/libaudit.c
+++ b/logd/libaudit.cpp
@@ -18,11 +18,13 @@
*
*/
+#include "libaudit.h"
+
#include <errno.h>
#include <string.h>
#include <unistd.h>
-#include "libaudit.h"
+#include <limits>
/**
* Waits for an ack from the kernel
@@ -32,22 +34,15 @@
* This function returns 0 on success, else -errno.
*/
static int get_ack(int fd) {
- int rc;
- struct audit_message rep;
-
- /* Sanity check, this is an internal interface this shouldn't happen */
- if (fd < 0) {
- return -EINVAL;
- }
-
- rc = audit_get_reply(fd, &rep, GET_REPLY_BLOCKING, MSG_PEEK);
+ struct audit_message rep = {};
+ int rc = audit_get_reply(fd, &rep, GET_REPLY_BLOCKING, MSG_PEEK);
if (rc < 0) {
return rc;
}
if (rep.nlh.nlmsg_type == NLMSG_ERROR) {
audit_get_reply(fd, &rep, GET_REPLY_BLOCKING, 0);
- rc = ((struct nlmsgerr*)rep.data)->error;
+ rc = reinterpret_cast<struct nlmsgerr*>(rep.data)->error;
if (rc) {
return -rc;
}
@@ -70,19 +65,11 @@
* This function returns a positive sequence number on success, else -errno.
*/
static int audit_send(int fd, int type, const void* data, size_t size) {
- int rc;
- static int16_t sequence = 0;
- struct audit_message req;
- struct sockaddr_nl addr;
-
- memset(&req, 0, sizeof(req));
- memset(&addr, 0, sizeof(addr));
-
- /* We always send netlink messaged */
- addr.nl_family = AF_NETLINK;
+ struct sockaddr_nl addr = {.nl_family = AF_NETLINK};
/* Set up the netlink headers */
- req.nlh.nlmsg_type = type;
+ struct audit_message req = {};
+ req.nlh.nlmsg_type = static_cast<uint16_t>(type);
req.nlh.nlmsg_len = NLMSG_SPACE(size);
req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
@@ -107,29 +94,23 @@
/*
* Only increment the sequence number on a guarantee
* you will send it to the kernel.
- *
- * Also, the sequence is defined as a u32 in the kernel
- * struct. Using an int here might not work on 32/64 bit splits. A
- * signed 64 bit value can overflow a u32..but a u32
- * might not fit in the response, so we need to use s32.
- * Which is still kind of hackish since int could be 16 bits
- * in size. The only safe type to use here is a signed 16
- * bit value.
*/
- req.nlh.nlmsg_seq = ++sequence;
+ static uint32_t sequence = 0;
+ if (sequence == std::numeric_limits<uint32_t>::max()) {
+ sequence = 1;
+ } else {
+ sequence++;
+ }
+ req.nlh.nlmsg_seq = sequence;
- /* While failing and its due to interrupts */
-
- rc = TEMP_FAILURE_RETRY(sendto(fd, &req, req.nlh.nlmsg_len, 0,
- (struct sockaddr*)&addr, sizeof(addr)));
+ ssize_t rc = TEMP_FAILURE_RETRY(
+ sendto(fd, &req, req.nlh.nlmsg_len, 0, (struct sockaddr*)&addr, sizeof(addr)));
/* Not all the bytes were sent */
if (rc < 0) {
- rc = -errno;
- goto out;
+ return -errno;
} else if ((uint32_t)rc != req.nlh.nlmsg_len) {
- rc = -EPROTO;
- goto out;
+ return -EPROTO;
}
/* We sent all the bytes, get the ack */
@@ -138,32 +119,22 @@
/* If the ack failed, return the error, else return the sequence number */
rc = (rc == 0) ? (int)sequence : rc;
-out:
- /* Don't let sequence roll to negative */
- if (sequence < 0) {
- sequence = 0;
- }
-
return rc;
}
int audit_setup(int fd, pid_t pid) {
- int rc;
- struct audit_message rep;
- struct audit_status status;
-
- memset(&status, 0, sizeof(status));
-
/*
* In order to set the auditd PID we send an audit message over the netlink
* socket with the pid field of the status struct set to our current pid,
* and the the mask set to AUDIT_STATUS_PID
*/
- status.pid = pid;
- status.mask = AUDIT_STATUS_PID;
+ struct audit_status status = {
+ .mask = AUDIT_STATUS_PID,
+ .pid = static_cast<uint32_t>(pid),
+ };
/* Let the kernel know this pid will be registering for audit events */
- rc = audit_send(fd, AUDIT_SET, &status, sizeof(status));
+ int rc = audit_send(fd, AUDIT_SET, &status, sizeof(status));
if (rc < 0) {
return rc;
}
@@ -178,6 +149,7 @@
* so I went to non-blocking and it seemed to fix the bug.
* Need to investigate further.
*/
+ struct audit_message rep = {};
audit_get_reply(fd, &rep, GET_REPLY_NONBLOCKING, 0);
return 0;
@@ -188,27 +160,18 @@
}
int audit_rate_limit(int fd, uint32_t limit) {
- struct audit_status status;
- memset(&status, 0, sizeof(status));
- status.mask = AUDIT_STATUS_RATE_LIMIT;
- status.rate_limit = limit; /* audit entries per second */
+ struct audit_status status = {
+ .mask = AUDIT_STATUS_RATE_LIMIT, .rate_limit = limit, /* audit entries per second */
+ };
return audit_send(fd, AUDIT_SET, &status, sizeof(status));
}
int audit_get_reply(int fd, struct audit_message* rep, reply_t block, int peek) {
- ssize_t len;
- int flags;
- int rc = 0;
-
- struct sockaddr_nl nladdr;
- socklen_t nladdrlen = sizeof(nladdr);
-
if (fd < 0) {
return -EBADF;
}
- /* Set up the flags for recv from */
- flags = (block == GET_REPLY_NONBLOCKING) ? MSG_DONTWAIT : 0;
+ int flags = (block == GET_REPLY_NONBLOCKING) ? MSG_DONTWAIT : 0;
flags |= peek;
/*
@@ -216,19 +179,20 @@
* the interface shows that EINTR can never be returned, other errors,
* however, can be returned.
*/
- len = TEMP_FAILURE_RETRY(recvfrom(fd, rep, sizeof(*rep), flags,
- (struct sockaddr*)&nladdr, &nladdrlen));
+ struct sockaddr_nl nladdr;
+ socklen_t nladdrlen = sizeof(nladdr);
+ ssize_t len = TEMP_FAILURE_RETRY(
+ recvfrom(fd, rep, sizeof(*rep), flags, (struct sockaddr*)&nladdr, &nladdrlen));
/*
* EAGAIN should be re-tried until success or another error manifests.
*/
if (len < 0) {
- rc = -errno;
- if (block == GET_REPLY_NONBLOCKING && rc == -EAGAIN) {
+ if (block == GET_REPLY_NONBLOCKING && errno == EAGAIN) {
/* If request is non blocking and errno is EAGAIN, just return 0 */
return 0;
}
- return rc;
+ return -errno;
}
if (nladdrlen != sizeof(nladdr)) {
@@ -242,10 +206,10 @@
/* Check if the reply from the kernel was ok */
if (!NLMSG_OK(&rep->nlh, (size_t)len)) {
- rc = (len == sizeof(*rep)) ? -EFBIG : -EBADE;
+ return len == sizeof(*rep) ? -EFBIG : -EBADE;
}
- return rc;
+ return 0;
}
void audit_close(int fd) {
diff --git a/logd/libaudit.h b/logd/libaudit.h
index b4a92a8..27b0866 100644
--- a/logd/libaudit.h
+++ b/logd/libaudit.h
@@ -17,8 +17,7 @@
* Written by William Roberts <w.roberts@sta.samsung.com>
*/
-#ifndef _LIBAUDIT_H_
-#define _LIBAUDIT_H_
+#pragma once
#include <stdint.h>
#include <sys/cdefs.h>
@@ -102,5 +101,3 @@
extern int audit_rate_limit(int fd, uint32_t limit);
__END_DECLS
-
-#endif
diff --git a/logd/tests/logd_test.cpp b/logd/logd_test.cpp
similarity index 97%
rename from logd/tests/logd_test.cpp
rename to logd/logd_test.cpp
index f47bee1..ed34ea4 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/logd_test.cpp
@@ -32,13 +32,14 @@
#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__
#include <selinux/selinux.h>
#endif
-#include "../LogReader.h" // pickup LOGD_SNDTIMEO
+#include "LogReader.h" // pickup LOGD_SNDTIMEO
#ifdef __ANDROID__
static void send_to_control(char* buf, size_t len) {
@@ -246,7 +247,7 @@
std::cerr << std::flush;
fflush(stdout);
fflush(stderr);
- EXPECT_EQ(sizeof(logger_entry), msg->entry.hdr_size);
+ EXPECT_GE(msg->entry.hdr_size, sizeof(logger_entry));
fprintf(stderr, "%s: [%u] ", prefix, msg->len());
fprintf(stderr, "hdr_size=%u ", msg->entry.hdr_size);
@@ -582,6 +583,7 @@
"dumpAndClose lids=0,1,2,3,4,5 timeout=6 start=0.000000000");
}
+#ifdef ENABLE_FLAKY_TESTS
// b/26447386 refined behavior
TEST(logd, timeout) {
#ifdef __ANDROID__
@@ -604,7 +606,7 @@
// A few tries to get it right just in case wrap kicks in due to
// content providers being active during the test.
int i = 5;
- log_time start(android_log_clockid());
+ log_time start(CLOCK_REALTIME);
start.tv_sec -= 30; // reach back a moderate period of time
while (--i) {
@@ -680,7 +682,7 @@
if (msg > start) {
start = msg;
start.tv_sec += 30;
- log_time now = log_time(android_log_clockid());
+ log_time now = log_time(CLOCK_REALTIME);
if (start > now) {
start = now;
--start.tv_sec;
@@ -716,6 +718,7 @@
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
}
+#endif
// b/27242723 confirmed fixed
TEST(logd, SNDTIMEO) {
@@ -870,10 +873,8 @@
ASSERT_EQ(0, info.si_status);
struct logger_list* logger_list;
- ASSERT_TRUE(nullptr !=
- (logger_list = android_logger_list_open(
- LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK,
- 0, pid)));
+ ASSERT_TRUE(nullptr != (logger_list = android_logger_list_open(LOG_ID_EVENTS,
+ ANDROID_LOG_NONBLOCK, 0, pid)));
int expected_count = (count < 2) ? count : 2;
int expected_chatty_count = (count <= 2) ? 0 : 1;
@@ -903,10 +904,10 @@
(log_msg.entry.len == (4 + 1 + 8))) {
if (tag != 0) continue;
- log_time tx(eventData + 4 + 1);
- if (ts == tx) {
+ log_time* tx = reinterpret_cast<log_time*>(eventData + 4 + 1);
+ if (ts == *tx) {
++count;
- } else if (ts1 == tx) {
+ } else if (ts1 == *tx) {
++second_count;
}
} else if (eventData[4] == EVENT_TYPE_STRING) {
diff --git a/logd/main.cpp b/logd/main.cpp
index 23bbf86..773ffb8 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -36,9 +36,10 @@
#include <memory>
+#include <android-base/logging.h>
#include <android-base/macros.h>
+#include <android-base/stringprintf.h>
#include <cutils/android_get_control_file.h>
-#include <cutils/properties.h>
#include <cutils/sockets.h>
#include <log/event_tag_map.h>
#include <packagelistparser/packagelistparser.h>
@@ -47,12 +48,17 @@
#include <processgroup/sched_policy.h>
#include <utils/threads.h>
+#include "ChattyLogBuffer.h"
#include "CommandListener.h"
#include "LogAudit.h"
#include "LogBuffer.h"
#include "LogKlog.h"
#include "LogListener.h"
+#include "LogReader.h"
+#include "LogStatistics.h"
+#include "LogTags.h"
#include "LogUtils.h"
+#include "SimpleLogBuffer.h"
#define KMSG_PRIORITY(PRI) \
'<', '0' + LOG_MAKEPRI(LOG_DAEMON, LOG_PRI(PRI)) / 10, \
@@ -66,19 +72,18 @@
sched_param param = {};
if (set_sched_policy(0, SP_BACKGROUND) < 0) {
- android::prdebug("failed to set background scheduling policy");
+ PLOG(ERROR) << "failed to set background scheduling policy";
return -1;
}
if (sched_setscheduler((pid_t)0, SCHED_BATCH, ¶m) < 0) {
- android::prdebug("failed to set batch scheduler");
+ PLOG(ERROR) << "failed to set batch scheduler";
return -1;
}
- if (!__android_logger_property_get_bool("ro.debuggable",
- BOOL_DEFAULT_FALSE) &&
+ if (!__android_logger_property_get_bool("ro.debuggable", BOOL_DEFAULT_FALSE) &&
prctl(PR_SET_DUMPABLE, 0) == -1) {
- android::prdebug("failed to clear PR_SET_DUMPABLE");
+ PLOG(ERROR) << "failed to clear PR_SET_DUMPABLE";
return -1;
}
@@ -101,99 +106,13 @@
return -1;
}
if (cap_set_proc(caps.get()) < 0) {
- android::prdebug("failed to set CAP_SYSLOG or CAP_AUDIT_CONTROL (%d)", errno);
+ PLOG(ERROR) << "failed to set CAP_SYSLOG or CAP_AUDIT_CONTROL";
return -1;
}
return 0;
}
-// Property helper
-static bool check_flag(const char* prop, const char* flag) {
- const char* cp = strcasestr(prop, flag);
- if (!cp) {
- return false;
- }
- // We only will document comma (,)
- static const char sep[] = ",:;|+ \t\f";
- if ((cp != prop) && !strchr(sep, cp[-1])) {
- return false;
- }
- cp += strlen(flag);
- return !*cp || !!strchr(sep, *cp);
-}
-
-static int fdDmesg = -1;
-void android::prdebug(const char* fmt, ...) {
- if (fdDmesg < 0) {
- return;
- }
-
- static const char message[] = {
- KMSG_PRIORITY(LOG_DEBUG), 'l', 'o', 'g', 'd', ':', ' '
- };
- char buffer[256];
- memcpy(buffer, message, sizeof(message));
-
- va_list ap;
- va_start(ap, fmt);
- int n = vsnprintf(buffer + sizeof(message),
- sizeof(buffer) - sizeof(message), fmt, ap);
- va_end(ap);
- if (n > 0) {
- buffer[sizeof(buffer) - 1] = '\0';
- if (!strchr(buffer, '\n')) {
- buffer[sizeof(buffer) - 2] = '\0';
- strlcat(buffer, "\n", sizeof(buffer));
- }
- write(fdDmesg, buffer, strlen(buffer));
- }
-}
-
-static sem_t reinit;
-static bool reinit_running = false;
-static LogBuffer* logBuf = nullptr;
-
-static void* reinit_thread_start(void* /*obj*/) {
- prctl(PR_SET_NAME, "logd.daemon");
-
- while (reinit_running && !sem_wait(&reinit) && reinit_running) {
- if (fdDmesg >= 0) {
- static const char reinit_message[] = { KMSG_PRIORITY(LOG_INFO),
- 'l',
- 'o',
- 'g',
- 'd',
- '.',
- 'd',
- 'a',
- 'e',
- 'm',
- 'o',
- 'n',
- ':',
- ' ',
- 'r',
- 'e',
- 'i',
- 'n',
- 'i',
- 't',
- '\n' };
- write(fdDmesg, reinit_message, sizeof(reinit_message));
- }
-
- // Anything that reads persist.<property>
- if (logBuf) {
- logBuf->init();
- logBuf->initPrune(nullptr);
- }
- android::ReReadEventLogTags();
- }
-
- return nullptr;
-}
-
char* android::uidToName(uid_t u) {
struct Userdata {
uid_t uid;
@@ -220,12 +139,6 @@
return userdata.name;
}
-// Serves as a global method to trigger reinitialization
-// and as a function that can be provided to signal().
-void reinit_signal_handler(int /*signal*/) {
- sem_post(&reinit);
-}
-
static void readDmesg(LogAudit* al, LogKlog* kl) {
if (!al && !kl) {
return;
@@ -250,10 +163,6 @@
}
buf[--len] = '\0';
- if (kl && kl->isMonotonic()) {
- kl->synchronize(buf.get(), len);
- }
-
ssize_t sublen;
for (char *ptr = nullptr, *tok = buf.get();
(rc >= 0) && !!(tok = android::log_strntok_r(tok, len, ptr, sublen));
@@ -311,8 +220,20 @@
return issueReinit();
}
+ android::base::InitLogging(
+ argv, [](android::base::LogId log_id, android::base::LogSeverity severity,
+ const char* tag, const char* file, unsigned int line, const char* message) {
+ if (tag && strcmp(tag, "logd") != 0) {
+ auto prefixed_message = android::base::StringPrintf("%s: %s", tag, message);
+ android::base::KernelLogger(log_id, severity, "logd", file, line,
+ prefixed_message.c_str());
+ } else {
+ android::base::KernelLogger(log_id, severity, "logd", file, line, message);
+ }
+ });
+
static const char dev_kmsg[] = "/dev/kmsg";
- fdDmesg = android_get_control_file(dev_kmsg);
+ int fdDmesg = android_get_control_file(dev_kmsg);
if (fdDmesg < 0) {
fdDmesg = TEMP_FAILURE_RETRY(open(dev_kmsg, O_WRONLY | O_CLOEXEC));
}
@@ -328,7 +249,7 @@
fdPmesg = TEMP_FAILURE_RETRY(
open(proc_kmsg, O_RDONLY | O_NDELAY | O_CLOEXEC));
}
- if (fdPmesg < 0) android::prdebug("Failed to open %s\n", proc_kmsg);
+ if (fdPmesg < 0) PLOG(ERROR) << "Failed to open " << proc_kmsg;
}
bool auditd = __android_logger_property_get_bool("ro.logd.auditd", BOOL_DEFAULT_TRUE);
@@ -336,49 +257,34 @@
return EXIT_FAILURE;
}
- // Reinit Thread
- sem_init(&reinit, 0, 0);
- pthread_attr_t attr;
- if (!pthread_attr_init(&attr)) {
- struct sched_param param;
+ // A cache of event log tags
+ LogTags log_tags;
- memset(¶m, 0, sizeof(param));
- pthread_attr_setschedparam(&attr, ¶m);
- pthread_attr_setschedpolicy(&attr, SCHED_BATCH);
- if (!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
- pthread_t thread;
- reinit_running = true;
- if (pthread_create(&thread, &attr, reinit_thread_start, nullptr)) {
- reinit_running = false;
- }
- }
- pthread_attr_destroy(&attr);
- }
+ // Pruning configuration.
+ PruneList prune_list;
+
+ // Partial (required for chatty) or full logging statistics.
+ bool enable_full_log_statistics = __android_logger_property_get_bool(
+ "logd.statistics", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_PERSIST |
+ BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE);
+ LogStatistics log_statistics(enable_full_log_statistics);
// Serves the purpose of managing the last logs times read on a
// socket connection, and as a reader lock on a range of log
// entries.
+ LogReaderList reader_list;
- LastLogTimes* times = new LastLogTimes();
-
- // LogBuffer is the object which is responsible for holding all
- // log entries.
-
- logBuf = new LogBuffer(times);
-
- signal(SIGHUP, reinit_signal_handler);
-
- if (__android_logger_property_get_bool(
- "logd.statistics", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_PERSIST |
- BOOL_DEFAULT_FLAG_ENG |
- BOOL_DEFAULT_FLAG_SVELTE)) {
- logBuf->enableStatistics();
+ // LogBuffer is the object which is responsible for holding all log entries.
+ LogBuffer* logBuf;
+ if (true) {
+ logBuf = new ChattyLogBuffer(&reader_list, &log_tags, &prune_list, &log_statistics);
+ } else {
+ logBuf = new SimpleLogBuffer(&reader_list, &log_tags, &log_statistics);
}
// LogReader listens on /dev/socket/logdr. When a client
// connects, log entries in the LogBuffer are written to the client.
-
- LogReader* reader = new LogReader(logBuf);
+ LogReader* reader = new LogReader(logBuf, &reader_list);
if (reader->startListener()) {
return EXIT_FAILURE;
}
@@ -386,17 +292,14 @@
// LogListener listens on /dev/socket/logdw for client
// initiated log messages. New log entries are added to LogBuffer
// and LogReader is notified to send updates to connected clients.
-
- LogListener* swl = new LogListener(logBuf, reader);
- // Backlog and /proc/sys/net/unix/max_dgram_qlen set to large value
- if (swl->startListener(600)) {
+ LogListener* swl = new LogListener(logBuf);
+ if (!swl->StartListener()) {
return EXIT_FAILURE;
}
// Command listener listens on /dev/socket/logd for incoming logd
// administrative commands.
-
- CommandListener* cl = new CommandListener(logBuf, reader, swl);
+ CommandListener* cl = new CommandListener(logBuf, &log_tags, &prune_list, &log_statistics);
if (cl->startListener()) {
return EXIT_FAILURE;
}
@@ -404,25 +307,22 @@
// LogAudit listens on NETLINK_AUDIT socket for selinux
// initiated log messages. New log entries are added to LogBuffer
// and LogReader is notified to send updates to connected clients.
-
LogAudit* al = nullptr;
if (auditd) {
- al = new LogAudit(logBuf, reader,
- __android_logger_property_get_bool(
- "ro.logd.auditd.dmesg", BOOL_DEFAULT_TRUE)
- ? fdDmesg
- : -1);
+ int dmesg_fd = __android_logger_property_get_bool("ro.logd.auditd.dmesg", BOOL_DEFAULT_TRUE)
+ ? fdDmesg
+ : -1;
+ al = new LogAudit(logBuf, dmesg_fd, &log_statistics);
}
LogKlog* kl = nullptr;
if (klogd) {
- kl = new LogKlog(logBuf, reader, fdDmesg, fdPmesg, al != nullptr);
+ kl = new LogKlog(logBuf, fdDmesg, fdPmesg, al != nullptr, &log_statistics);
}
readDmesg(al, kl);
// failure is an option ... messages are in dmesg (required by standard)
-
if (kl && kl->startListener()) {
delete kl;
}
diff --git a/logd/rwlock.h b/logd/rwlock.h
new file mode 100644
index 0000000..2b27ff1
--- /dev/null
+++ b/logd/rwlock.h
@@ -0,0 +1,56 @@
+/*
+ * 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/LICENSE2.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>
+
+#include <android-base/macros.h>
+#include <android-base/thread_annotations.h>
+
+// As of the end of May 2020, 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 RAII thread wrappers.
+
+class SHARED_CAPABILITY("mutex") RwLock {
+ public:
+ RwLock() {}
+ ~RwLock() {}
+
+ void lock() ACQUIRE() { pthread_rwlock_wrlock(&rwlock_); }
+ void lock_shared() ACQUIRE_SHARED() { pthread_rwlock_rdlock(&rwlock_); }
+
+ void unlock() RELEASE() { pthread_rwlock_unlock(&rwlock_); }
+
+ private:
+ pthread_rwlock_t rwlock_ = PTHREAD_RWLOCK_INITIALIZER;
+};
+
+// std::shared_lock does not have thread annotations, so we need our own.
+
+class SCOPED_CAPABILITY SharedLock {
+ public:
+ SharedLock(RwLock& lock) ACQUIRE_SHARED(lock) : lock_(lock) { lock_.lock_shared(); }
+ ~SharedLock() RELEASE() { lock_.unlock(); }
+
+ void lock_shared() ACQUIRE_SHARED() { lock_.lock_shared(); }
+ void unlock() RELEASE() { lock_.unlock(); }
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(SharedLock);
+
+ private:
+ RwLock& lock_;
+};
diff --git a/logd/tests/Android.bp b/logd/tests/Android.bp
deleted file mode 100644
index d39da8a..0000000
--- a/logd/tests/Android.bp
+++ /dev/null
@@ -1,68 +0,0 @@
-//
-// Copyright (C) 2014 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-// -----------------------------------------------------------------------------
-// Unit tests.
-// -----------------------------------------------------------------------------
-
-cc_defaults {
- name: "logd-unit-test-defaults",
-
- cflags: [
- "-fstack-protector-all",
- "-g",
- "-Wall",
- "-Wextra",
- "-Werror",
- "-fno-builtin",
-
- "-DAUDITD_LOG_TAG=1003",
- "-DCHATTY_LOG_TAG=1004",
- ],
-
- srcs: ["logd_test.cpp"],
-
- static_libs: [
- "libbase",
- "libcutils",
- "libselinux",
- "liblog",
- ],
-}
-
-// Build tests for the logger. Run with:
-// adb shell /data/nativetest/logd-unit-tests/logd-unit-tests
-cc_test {
- name: "logd-unit-tests",
- defaults: ["logd-unit-test-defaults"],
-}
-
-cc_test {
- name: "CtsLogdTestCases",
- defaults: ["logd-unit-test-defaults"],
- multilib: {
- lib32: {
- suffix: "32",
- },
- lib64: {
- suffix: "64",
- },
- },
- test_suites: [
- "cts",
- "vts",
- ],
-}
diff --git a/qemu_pipe/Android.bp b/qemu_pipe/Android.bp
index c6bda4a..ad86a4e 100644
--- a/qemu_pipe/Android.bp
+++ b/qemu_pipe/Android.bp
@@ -4,6 +4,11 @@
name: "libqemu_pipe",
vendor_available: true,
recovery_available: true,
+ apex_available: [
+ "com.android.adbd",
+ // TODO(b/151398197) remove the below
+ "//apex_available:platform",
+ ],
sanitize: {
misc_undefined: ["integer"],
},
diff --git a/rootdir/etc/public.libraries.android.txt b/rootdir/etc/public.libraries.android.txt
index 405f5a9..5de422f 100644
--- a/rootdir/etc/public.libraries.android.txt
+++ b/rootdir/etc/public.libraries.android.txt
@@ -16,6 +16,7 @@
liblog.so
libmediandk.so
libm.so
+libnativehelper.so
libnativewindow.so
libneuralnetworks.so nopreload
libOpenMAXAL.so
diff --git a/rootdir/etc/public.libraries.iot.txt b/rootdir/etc/public.libraries.iot.txt
index b565340..77f8bb8 100644
--- a/rootdir/etc/public.libraries.iot.txt
+++ b/rootdir/etc/public.libraries.iot.txt
@@ -17,6 +17,7 @@
liblog.so
libmediandk.so
libm.so
+libnativehelper.so
libnativewindow.so
libneuralnetworks.so
libOpenMAXAL.so
diff --git a/rootdir/etc/public.libraries.wear.txt b/rootdir/etc/public.libraries.wear.txt
index 7cbda08..82196e4 100644
--- a/rootdir/etc/public.libraries.wear.txt
+++ b/rootdir/etc/public.libraries.wear.txt
@@ -16,6 +16,7 @@
liblog.so
libmediandk.so
libm.so
+libnativehelper.so
libnativewindow.so
libneuralnetworks.so
libOpenMAXAL.so
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 276cc1e..427ac4b 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -16,6 +16,11 @@
# Disable sysrq from keyboard
write /proc/sys/kernel/sysrq 0
+ # Android doesn't need kernel module autoloading, and it causes SELinux
+ # denials. So disable it by setting modprobe to the empty string. Note: to
+ # explicitly set a sysctl to an empty string, a trailing newline is needed.
+ write /proc/sys/kernel/modprobe \n
+
# Set the security context of /adb_keys if present.
restorecon /adb_keys
@@ -73,6 +78,9 @@
mkdir /dev/boringssl 0755 root root
mkdir /dev/boringssl/selftest 0755 root root
+ # Mount tracefs
+ mount tracefs tracefs /sys/kernel/tracing
+
# Run boringssl self test for each ABI so that later processes can skip it. http://b/139348610
on early-init && property:ro.product.cpu.abilist32=*
exec_start boringssl_self_test32
@@ -154,7 +162,7 @@
chmod 0664 /dev/blkio/tasks
chmod 0664 /dev/blkio/background/tasks
write /dev/blkio/blkio.weight 1000
- write /dev/blkio/background/blkio.weight 500
+ write /dev/blkio/background/blkio.weight 200
write /dev/blkio/blkio.group_idle 0
write /dev/blkio/background/blkio.group_idle 0
@@ -539,8 +547,8 @@
enter_default_mount_ns
# /data/apex is now available. Start apexd to scan and activate APEXes.
- mkdir /data/apex 0750 root system encryption=None
- mkdir /data/apex/active 0750 root system
+ mkdir /data/apex 0755 root system encryption=None
+ mkdir /data/apex/active 0755 root system
mkdir /data/apex/backup 0700 root system
mkdir /data/apex/hashtree 0700 root system
mkdir /data/apex/sessions 0700 root system
@@ -701,9 +709,7 @@
# A tmpfs directory, which will contain all apps CE DE data directory that
# bind mount from the original source.
- chown root root /data_mirror
- chmod 0700 /data_mirror
- mount tmpfs tmpfs /data_mirror mode=0700,uid=0,gid=1000 nodev noexec nosuid
+ mount tmpfs tmpfs /data_mirror nodev noexec nosuid mode=0700,uid=0,gid=1000
restorecon /data_mirror
mkdir /data_mirror/data_ce 0700 root root
mkdir /data_mirror/data_de 0700 root root
@@ -732,6 +738,13 @@
# Create root dir for Incremental Service
mkdir /data/incremental 0771 system system encryption=Require
+ # Create directories for statsd
+ mkdir /data/misc/stats-active-metric/ 0770 statsd system
+ mkdir /data/misc/stats-data/ 0770 statsd system
+ mkdir /data/misc/stats-metadata/ 0770 statsd system
+ mkdir /data/misc/stats-service/ 0770 statsd system
+ mkdir /data/misc/train-info/ 0770 statsd system
+
# Wait for apexd to finish activating APEXes before starting more processes.
wait_for_prop apexd.status activated
perform_apex_config
@@ -815,6 +828,11 @@
start zygote
start zygote_secondary
+on boot && property:ro.config.low_ram=true
+ # Tweak background writeout
+ write /proc/sys/vm/dirty_expire_centisecs 200
+ write /proc/sys/vm/dirty_background_ratio 5
+
on boot
# basic network init
ifup lo
@@ -836,11 +854,7 @@
chown root system /sys/block/zram0/writeback
chmod 0664 /sys/block/zram0/writeback
- # Tweak background writeout
- write /proc/sys/vm/dirty_expire_centisecs 200
- write /proc/sys/vm/dirty_background_ratio 5
-
- # F2FS tuning. Set cp_interval larger than dirty_expire_centisecs
+ # F2FS tuning. Set cp_interval larger than dirty_expire_centisecs, 30 secs,
# to avoid power consumption when system becomes mostly idle. Be careful
# to make it too large, since it may bring userdata loss, if they
# are not aware of using fsync()/sync() to prepare sudden power-cut.
@@ -1052,6 +1066,12 @@
start vold
exec - system system -- /system/bin/vdc checkpoint resetCheckpoint
exec - system system -- /system/bin/vdc checkpoint markBootAttempt
+ # Unmount /data_mirror mounts in the reverse order of corresponding mounts.
+ umount /data_mirror/data_ce/null/0
+ umount /data_mirror/data_ce/null
+ umount /data_mirror/data_de/null
+ umount /data_mirror/cur_profiles
+ umount /data_mirror
remount_userdata
start bootanim
diff --git a/run-as/run-as.cpp b/run-as/run-as.cpp
index 432c434..cc92c68 100644
--- a/run-as/run-as.cpp
+++ b/run-as/run-as.cpp
@@ -247,12 +247,12 @@
std::string seinfo = std::string(info.seinfo) + ":fromRunAs";
if (selinux_android_setcontext(uid, 0, seinfo.c_str(), pkgname) < 0) {
- error(1, errno, "couldn't set SELinux security context");
+ error(1, errno, "couldn't set SELinux security context '%s'", seinfo.c_str());
}
// cd into the data directory, and set $HOME correspondingly.
if (TEMP_FAILURE_RETRY(chdir(info.data_dir)) == -1) {
- error(1, errno, "couldn't chdir to package's data directory");
+ error(1, errno, "couldn't chdir to package's data directory '%s'", info.data_dir);
}
setenv("HOME", info.data_dir, 1);
diff --git a/storaged/main.cpp b/storaged/main.cpp
index a7bda14..bbed210 100644
--- a/storaged/main.cpp
+++ b/storaged/main.cpp
@@ -71,6 +71,7 @@
bool flag_dump_perf = false;
int opt;
+ signal(SIGPIPE, SIG_IGN);
android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));
for (;;) {
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
+}
diff --git a/trusty/keymaster/4.0/android.hardware.keymaster@4.0-service.trusty.xml b/trusty/keymaster/4.0/android.hardware.keymaster@4.0-service.trusty.xml
index aa30707..65eb854 100644
--- a/trusty/keymaster/4.0/android.hardware.keymaster@4.0-service.trusty.xml
+++ b/trusty/keymaster/4.0/android.hardware.keymaster@4.0-service.trusty.xml
@@ -2,10 +2,6 @@
<hal format="hidl">
<name>android.hardware.keymaster</name>
<transport>hwbinder</transport>
- <version>4.0</version>
- <interface>
- <name>IKeymasterDevice</name>
- <instance>default</instance>
- </interface>
+ <fqname>@4.0::IKeymasterDevice/default</fqname>
</hal>
</manifest>