Merge "Support casefolded encryption"
diff --git a/Android.bp b/Android.bp
index c6f6251..0b4a925 100644
--- a/Android.bp
+++ b/Android.bp
@@ -2,5 +2,3 @@
name: "android_filesystem_config_header",
srcs: ["include/private/android_filesystem_config.h"],
}
-
-subdirs = ["*"]
diff --git a/adb/Android.bp b/adb/Android.bp
index bd1f124..b39defe 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -217,6 +217,7 @@
"libcutils",
"libcrypto_utils",
"libcrypto",
+ "liblog",
"libmdnssd",
"libdiagnose_usb",
"libusb",
@@ -729,39 +730,3 @@
"fastdeploy/testdata/sample.cd",
],
}
-
-prebuilt_etc {
- name: "com.android.adbd.ld.config.txt",
- src: "apex/ld.config.txt",
- filename: "ld.config.txt",
- installable: false,
-}
-
-apex {
- name: "com.android.adbd",
- manifest: "apex/apex_manifest.json",
-
- binaries: ["adbd"],
- prebuilts: ["com.android.adbd.init.rc", "com.android.adbd.ld.config.txt"],
-
- key: "com.android.adbd.key",
- certificate: ":com.android.adbd.certificate",
-}
-
-apex_key {
- name: "com.android.adbd.key",
- public_key: "apex/com.android.adbd.avbpubkey",
- private_key: "apex/com.android.adbd.pem",
-}
-
-android_app_certificate {
- name: "com.android.adbd.certificate",
- certificate: "apex/com.android.adbd",
-}
-
-prebuilt_etc {
- name: "com.android.adbd.init.rc",
- src: "apex/adbd.rc",
- filename: "init.rc",
- installable: false,
-}
diff --git a/adb/adb.h b/adb/adb.h
index e7fcc91..7f7dd0d 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -185,14 +185,7 @@
} while (0)
#endif
-#if ADB_HOST_ON_TARGET
-/* adb and adbd are coexisting on the target, so use 5038 for adb
- * to avoid conflicting with adbd's usage of 5037
- */
-#define DEFAULT_ADB_PORT 5038
-#else
#define DEFAULT_ADB_PORT 5037
-#endif
#define DEFAULT_ADB_LOCAL_TRANSPORT_PORT 5555
diff --git a/adb/apex/Android.bp b/adb/apex/Android.bp
new file mode 100644
index 0000000..40ea448
--- /dev/null
+++ b/adb/apex/Android.bp
@@ -0,0 +1,49 @@
+apex_defaults {
+ name: "com.android.adbd-defaults",
+
+ binaries: ["adbd"],
+ prebuilts: ["com.android.adbd.init.rc", "com.android.adbd.ld.config.txt"],
+
+ key: "com.android.adbd.key",
+ certificate: ":com.android.adbd.certificate",
+}
+
+apex {
+ name: "com.android.adbd",
+ defaults: ["com.android.adbd-defaults"],
+ manifest: "apex_manifest.json",
+}
+
+// adbd apex with INT_MAX version code, to allow for upgrade/rollback testing.
+apex {
+ name: "test_com.android.adbd",
+ defaults: ["com.android.adbd-defaults"],
+ manifest: "test_apex_manifest.json",
+ file_contexts: ":com.android.adbd-file_contexts",
+ installable: false,
+}
+
+prebuilt_etc {
+ name: "com.android.adbd.ld.config.txt",
+ src: "ld.config.txt",
+ filename: "ld.config.txt",
+ installable: false,
+}
+
+prebuilt_etc {
+ name: "com.android.adbd.init.rc",
+ src: "adbd.rc",
+ filename: "init.rc",
+ installable: false,
+}
+
+apex_key {
+ name: "com.android.adbd.key",
+ public_key: "com.android.adbd.avbpubkey",
+ private_key: "com.android.adbd.pem",
+}
+
+android_app_certificate {
+ name: "com.android.adbd.certificate",
+ certificate: "com.android.adbd",
+}
diff --git a/adb/apex/ld.config.txt b/adb/apex/ld.config.txt
index 13d66b6..d1858a4 100644
--- a/adb/apex/ld.config.txt
+++ b/adb/apex/ld.config.txt
@@ -10,6 +10,8 @@
namespace.default.isolated = true
namespace.default.search.paths = /apex/com.android.adbd/${LIB}
namespace.default.asan.search.paths = /apex/com.android.adbd/${LIB}
+namespace.default.permitted.paths = /system/${LIB}
+namespace.default.asan.permitted.paths = /system/${LIB}
namespace.default.links = art,platform
namespace.default.link.art.shared_libs = libadbconnection_server.so
namespace.default.link.platform.shared_libs = libc.so:libdl.so:libm.so:libclang_rt.hwasan-aarch64-android.so
diff --git a/adb/apex/test_apex_manifest.json b/adb/apex/test_apex_manifest.json
new file mode 100644
index 0000000..7131977
--- /dev/null
+++ b/adb/apex/test_apex_manifest.json
@@ -0,0 +1,4 @@
+{
+ "name": "com.android.adbd",
+ "version": 2147483647
+}
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 813a8a9..a6d7e31 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -790,6 +790,15 @@
service_string);
}
+static int adb_shell_noinput(int argc, const char** argv) {
+#if !defined(_WIN32)
+ unique_fd fd(adb_open("/dev/null", O_RDONLY));
+ CHECK_NE(STDIN_FILENO, fd.get());
+ dup2(fd.get(), STDIN_FILENO);
+#endif
+ return adb_shell(argc, argv);
+}
+
static int adb_sideload_legacy(const char* filename, int in_fd, int size) {
std::string error;
unique_fd out_fd(adb_connect(android::base::StringPrintf("sideload:%d", size), &error));
@@ -1116,8 +1125,8 @@
return false;
}
+ fwrite(buf, 1, sizeof(buf) - bytes_left, stdout);
fflush(stdout);
- WriteFdExactly(STDOUT_FILENO, buf, sizeof(buf) - bytes_left);
if (cur != buf && strstr(buf, "restarting") == nullptr) {
return true;
}
@@ -1612,7 +1621,7 @@
return adb_query_command(query);
}
else if (!strcmp(argv[0], "connect")) {
- if (argc != 2) error_exit("usage: adb connect HOST[:PORT>]");
+ 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);
@@ -1711,7 +1720,7 @@
if (CanUseFeature(features, kFeatureRemountShell)) {
std::vector<const char*> args = {"shell"};
args.insert(args.cend(), argv, argv + argc);
- return adb_shell(args.size(), args.data());
+ return adb_shell_noinput(args.size(), args.data());
} else if (argc > 1) {
auto command = android::base::StringPrintf("%s:%s", argv[0], argv[1]);
return adb_connect_command(command);
diff --git a/base/Android.bp b/base/Android.bp
index 8351461..b25d0df 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -56,6 +56,7 @@
"chrono_utils.cpp",
"cmsg.cpp",
"file.cpp",
+ "liblog_symbols.cpp",
"logging.cpp",
"mapped_file.cpp",
"parsebool.cpp",
@@ -68,6 +69,10 @@
"test_utils.cpp",
],
+ static: {
+ cflags: ["-DNO_LIBLOG_DLSYM"],
+ },
+
cppflags: ["-Wexit-time-destructors"],
shared_libs: ["liblog"],
target: {
diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h
index ab6476c..cc162cd 100644
--- a/base/include/android-base/logging.h
+++ b/base/include/android-base/logging.h
@@ -85,7 +85,7 @@
INFO,
WARNING,
ERROR,
- FATAL_WITHOUT_ABORT,
+ FATAL_WITHOUT_ABORT, // For loggability tests, this is considered identical to FATAL.
FATAL,
};
@@ -93,6 +93,8 @@
DEFAULT,
MAIN,
SYSTEM,
+ RADIO,
+ CRASH,
};
using LogFunction = std::function<void(LogId, LogSeverity, const char*, const char*,
@@ -113,10 +115,8 @@
void DefaultAborter(const char* abort_message);
-std::string GetDefaultTag();
void SetDefaultTag(const std::string& tag);
-#ifdef __ANDROID__
// 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 {
@@ -129,7 +129,6 @@
private:
LogId default_log_id_;
};
-#endif
// Configure logging based on ANDROID_LOG_TAGS environment variable.
// We need to parse a string that looks like
@@ -211,8 +210,8 @@
#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((SEVERITY_LAMBDA(severity)) >= ::android::base::GetMinimumLogSeverity()) || \
+#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
@@ -222,20 +221,16 @@
// 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) LOG_STREAM_TO(DEFAULT, severity)
-
-// Get an ostream that can be used for logging at the given severity and to the
-// given destination. The same notes as for LOG_STREAM apply.
-#define LOG_STREAM_TO(dest, severity) \
- ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::dest, \
- SEVERITY_LAMBDA(severity), _LOG_TAG_INTERNAL, -1) \
+#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) LOG_TO(DEFAULT, severity)
+#define LOG(severity) LOGGING_PREAMBLE(severity) && LOG_STREAM(severity)
// Checks if we want to log something, and sets up appropriate RAII objects if
// so.
@@ -245,21 +240,12 @@
ABORT_AFTER_LOG_EXPR_IF((SEVERITY_LAMBDA(severity)) == ::android::base::FATAL, true) && \
::android::base::ErrnoRestorer())
-// Logs a message to logcat with the specified log ID on Android otherwise to
-// stderr. If the severity is FATAL it also causes an abort.
-// Use an expression here so we can support the << operator following the macro,
-// like "LOG(DEBUG) << xxx;".
-#define LOG_TO(dest, severity) LOGGING_PREAMBLE(severity) && LOG_STREAM_TO(dest, severity)
-
// A variant of LOG that also logs the current errno value. To be used when
// library calls fail.
-#define PLOG(severity) PLOG_TO(DEFAULT, severity)
-
-// Behaves like PLOG, but logs to the specified log ID.
-#define PLOG_TO(dest, severity) \
- LOGGING_PREAMBLE(severity) && \
- ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::dest, \
- SEVERITY_LAMBDA(severity), _LOG_TAG_INTERNAL, errno) \
+#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.
@@ -272,24 +258,23 @@
//
// 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::DEFAULT, \
- ::android::base::FATAL, _LOG_TAG_INTERNAL, -1) \
- .stream() \
+#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::DEFAULT, \
- ::android::base::FATAL, _LOG_TAG_INTERNAL, -1) \
- .stream() \
- << "Check failed: " << #LHS << " " << #OP << " " << #RHS << " (" #LHS "=" << _values.lhs \
+#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
@@ -311,8 +296,8 @@
#define CHECK_STROP(s1, s2, sense) \
while (UNLIKELY((strcmp(s1, s2) == 0) != (sense))) \
ABORT_AFTER_LOG_FATAL \
- ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \
- ::android::base::FATAL, _LOG_TAG_INTERNAL, -1) \
+ ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::FATAL, \
+ _LOG_TAG_INTERNAL, -1) \
.stream() \
<< "Check failed: " << "\"" << (s1) << "\"" \
<< ((sense) ? " == " : " != ") << "\"" << (s2) << "\""
@@ -431,8 +416,10 @@
// of a CHECK. The destructor will abort if the severity is FATAL.
class LogMessage {
public:
- LogMessage(const char* file, unsigned int line, LogId id, LogSeverity severity, const char* tag,
+ // 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();
@@ -441,8 +428,8 @@
std::ostream& stream();
// The routine that performs the actual logging.
- static void LogLine(const char* file, unsigned int line, LogId id, LogSeverity severity,
- const char* tag, const char* msg);
+ static void LogLine(const char* file, unsigned int line, LogSeverity severity, const char* tag,
+ const char* msg);
private:
const std::unique_ptr<LogMessageData> data_;
@@ -456,6 +443,9 @@
// 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:
@@ -474,9 +464,6 @@
// 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: lots of ifdef magic to make this work with Clang (platform) vs GCC (windows tools)
-// Note: using diagnose_if(true) under Clang and nothing under GCC/mingw as there is no common
-// attribute support.
// 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.
@@ -486,8 +473,8 @@
#pragma clang diagnostic ignored "-Wgcc-compat"
#define OSTREAM_STRING_POINTER_USAGE_WARNING \
__attribute__((diagnose_if(true, "Unexpected logging of string pointer", "warning")))
-inline std::ostream& operator<<(std::ostream& stream, const std::string* string_pointer)
- OSTREAM_STRING_POINTER_USAGE_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
diff --git a/base/include/android-base/unique_fd.h b/base/include/android-base/unique_fd.h
index 1605daf..c4a0aad 100644
--- a/base/include/android-base/unique_fd.h
+++ b/base/include/android-base/unique_fd.h
@@ -116,6 +116,8 @@
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;
diff --git a/base/liblog_symbols.cpp b/base/liblog_symbols.cpp
new file mode 100644
index 0000000..d5dfcd2
--- /dev/null
+++ b/base/liblog_symbols.cpp
@@ -0,0 +1,85 @@
+/*
+ * 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
new file mode 100644
index 0000000..d3134e9
--- /dev/null
+++ b/base/liblog_symbols.h
@@ -0,0 +1,47 @@
+/*
+ * 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
index f89168c..1d8ef57 100644
--- a/base/logging.cpp
+++ b/base/logging.cpp
@@ -36,17 +36,18 @@
#include <sys/uio.h>
#endif
+#include <atomic>
#include <iostream>
#include <limits>
#include <mutex>
+#include <optional>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
-// Headers for LogMessage::LogLine.
-#ifdef __ANDROID__
#include <android/log.h>
+#ifdef __ANDROID__
#include <android/set_abort_message.h>
#else
#include <sys/types.h>
@@ -59,6 +60,8 @@
#include <android-base/strings.h>
#include <android-base/threads.h>
+#include "liblog_symbols.h"
+
namespace android {
namespace base {
@@ -115,11 +118,84 @@
}
#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());
@@ -129,35 +205,39 @@
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;
-std::string GetDefaultTag() {
- std::lock_guard<std::recursive_mutex> lock(TagLock());
- if (gDefaultTag == nullptr) {
- return "";
- }
- return *gDefaultTag;
-}
+
void SetDefaultTag(const std::string& tag) {
- std::lock_guard<std::recursive_mutex> lock(TagLock());
- if (gDefaultTag != nullptr) {
- delete gDefaultTag;
- gDefaultTag = nullptr;
- }
- if (!tag.empty()) {
- gDefaultTag = new 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__)
@@ -218,8 +298,13 @@
static_assert(arraysize(log_characters) - 1 == FATAL + 1,
"Mismatch in size of log_characters and values in LogSeverity");
char severity_char = log_characters[severity];
- fprintf(stderr, "%s %c %s %5d %5" PRIu64 " %s:%u] %s\n", tag ? tag : "nullptr", severity_char,
- timestamp, getpid(), GetThreadId(), file, line, message);
+ 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*/,
@@ -242,41 +327,35 @@
}
-#ifdef __ANDROID__
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) {
- static constexpr android_LogPriority kLogSeverityToAndroidLogPriority[] = {
- ANDROID_LOG_VERBOSE, ANDROID_LOG_DEBUG, ANDROID_LOG_INFO,
- ANDROID_LOG_WARN, ANDROID_LOG_ERROR, ANDROID_LOG_FATAL,
- ANDROID_LOG_FATAL,
- };
- static_assert(arraysize(kLogSeverityToAndroidLogPriority) == FATAL + 1,
- "Mismatch in size of kLogSeverityToAndroidLogPriority and values in LogSeverity");
-
- int priority = kLogSeverityToAndroidLogPriority[severity];
+ android_LogPriority priority = LogSeverityToPriority(severity);
if (id == DEFAULT) {
id = default_log_id_;
}
- static constexpr log_id kLogIdToAndroidLogId[] = {
- LOG_ID_MAX, LOG_ID_MAIN, LOG_ID_SYSTEM,
- };
- static_assert(arraysize(kLogIdToAndroidLogId) == SYSTEM + 1,
- "Mismatch in size of kLogIdToAndroidLogId and values in LogId");
- log_id lg_id = kLogIdToAndroidLogId[id];
+ int lg_id = LogIdTolog_id_t(id);
- if (priority == ANDROID_LOG_FATAL) {
- __android_log_buf_print(lg_id, priority, tag, "%s:%u] %s", file, line,
- message);
+ char log_message[1024];
+ if (priority == ANDROID_LOG_FATAL && file != nullptr) {
+ snprintf(log_message, sizeof(log_message), "%s:%u] %s", file, line, message);
+ } else {
+ snprintf(log_message, sizeof(log_message), "%s", message);
+ }
+
+ 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, log_message);
} else {
__android_log_buf_print(lg_id, priority, tag, "%s", message);
}
}
-#endif
void InitLogging(char* argv[], LogFunction&& logger, AbortFunction&& aborter) {
SetLogger(std::forward<LogFunction>(logger));
@@ -307,27 +386,27 @@
if (spec.size() == 3 && StartsWith(spec, "*:")) {
switch (spec[2]) {
case 'v':
- gMinimumLogSeverity = VERBOSE;
+ SetMinimumLogSeverity(VERBOSE);
continue;
case 'd':
- gMinimumLogSeverity = DEBUG;
+ SetMinimumLogSeverity(DEBUG);
continue;
case 'i':
- gMinimumLogSeverity = INFO;
+ SetMinimumLogSeverity(INFO);
continue;
case 'w':
- gMinimumLogSeverity = WARNING;
+ SetMinimumLogSeverity(WARNING);
continue;
case 'e':
- gMinimumLogSeverity = ERROR;
+ SetMinimumLogSeverity(ERROR);
continue;
case 'f':
- gMinimumLogSeverity = FATAL_WITHOUT_ABORT;
+ SetMinimumLogSeverity(FATAL_WITHOUT_ABORT);
continue;
// liblog will even suppress FATAL if you say 's' for silent, but that's
// crazy!
case 's':
- gMinimumLogSeverity = FATAL_WITHOUT_ABORT;
+ SetMinimumLogSeverity(FATAL_WITHOUT_ABORT);
continue;
}
}
@@ -337,24 +416,56 @@
}
void SetLogger(LogFunction&& logger) {
- std::lock_guard<std::mutex> lock(LoggingLock());
- Logger() = std::move(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) {
- std::lock_guard<std::mutex> lock(LoggingLock());
- Aborter() = std::move(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, LogId id, LogSeverity severity,
- const char* tag, int error)
+ LogMessageData(const char* file, unsigned int line, LogSeverity severity, const char* tag,
+ int error)
: file_(GetFileBasename(file)),
line_number_(line),
- id_(id),
severity_(severity),
tag_(tag),
error_(error) {}
@@ -373,10 +484,6 @@
const char* GetTag() const { return tag_; }
- LogId GetId() const {
- return id_;
- }
-
int GetError() const {
return error_;
}
@@ -393,7 +500,6 @@
std::ostringstream buffer_;
const char* const file_;
const unsigned int line_number_;
- const LogId id_;
const LogSeverity severity_;
const char* const tag_;
const int error_;
@@ -401,9 +507,13 @@
DISALLOW_COPY_AND_ASSIGN(LogMessageData);
};
-LogMessage::LogMessage(const char* file, unsigned int line, LogId id, LogSeverity severity,
+LogMessage::LogMessage(const char* file, unsigned int line, LogId, LogSeverity severity,
const char* tag, int error)
- : data_(new LogMessageData(file, line, id, severity, tag, 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.
@@ -429,16 +539,16 @@
// 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_->GetId(), data_->GetSeverity(),
- data_->GetTag(), msg.c_str());
+ 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_->GetId(), data_->GetSeverity(),
- data_->GetTag(), &msg[i]);
+ 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;
@@ -448,7 +558,12 @@
// Abort if necessary.
if (data_->GetSeverity() == FATAL) {
- Aborter()(msg.c_str());
+ static auto& liblog_functions = GetLibLogFunctions();
+ if (liblog_functions) {
+ liblog_functions->__android_log_call_aborter(msg.c_str());
+ } else {
+ Aborter()(msg.c_str());
+ }
}
}
@@ -456,27 +571,61 @@
return data_->GetBuffer();
}
-void LogMessage::LogLine(const char* file, unsigned int line, LogId id, LogSeverity severity,
- const char* tag, const char* message) {
- if (tag == nullptr) {
- std::lock_guard<std::recursive_mutex> lock(TagLock());
- if (gDefaultTag == nullptr) {
- gDefaultTag = new std::string(getprogname());
- }
- Logger()(id, severity, gDefaultTag->c_str(), file, line, message);
+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 {
- Logger()(id, severity, tag, file, line, message);
+ 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) {
+ // TODO: It is safe to pass nullptr for tag, but it will be better to use the default log tag.
+ int priority = LogSeverityToPriority(severity);
+ return __android_log_is_loggable(priority, tag, ANDROID_LOG_INFO);
+ } else {
+ return severity >= gMinimumLogSeverity;
+ }
}
LogSeverity SetMinimumLogSeverity(LogSeverity new_severity) {
- LogSeverity old_severity = gMinimumLogSeverity;
- gMinimumLogSeverity = new_severity;
- return old_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) {
diff --git a/base/logging_test.cpp b/base/logging_test.cpp
index 3113fb4..3a453e6 100644
--- a/base/logging_test.cpp
+++ b/base/logging_test.cpp
@@ -140,10 +140,6 @@
CHECK_WOULD_LOG_ENABLED(FATAL);
}
-TEST(logging, WOULD_LOG_FATAL_WITHOUT_ABORT_disabled) {
- CHECK_WOULD_LOG_DISABLED(FATAL_WITHOUT_ABORT);
-}
-
TEST(logging, WOULD_LOG_FATAL_WITHOUT_ABORT_enabled) {
CHECK_WOULD_LOG_ENABLED(FATAL_WITHOUT_ABORT);
}
@@ -266,10 +262,6 @@
CheckMessage(cap2, android::base::severity, "foobar"); \
} \
-TEST(logging, LOG_STREAM_FATAL_WITHOUT_ABORT_disabled) {
- CHECK_LOG_STREAM_DISABLED(FATAL_WITHOUT_ABORT);
-}
-
TEST(logging, LOG_STREAM_FATAL_WITHOUT_ABORT_enabled) {
ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(FATAL_WITHOUT_ABORT));
}
@@ -352,10 +344,6 @@
ASSERT_DEATH({SuppressAbortUI(); LOG(::android::base::FATAL) << "foobar";}, "foobar");
}
-TEST(logging, LOG_FATAL_WITHOUT_ABORT_disabled) {
- CHECK_LOG_DISABLED(FATAL_WITHOUT_ABORT);
-}
-
TEST(logging, LOG_FATAL_WITHOUT_ABORT_enabled) {
ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(FATAL_WITHOUT_ABORT));
}
@@ -508,10 +496,6 @@
ASSERT_DEATH({SuppressAbortUI(); PLOG(::android::base::FATAL) << "foobar";}, "foobar");
}
-TEST(logging, PLOG_FATAL_WITHOUT_ABORT_disabled) {
- CHECK_PLOG_DISABLED(FATAL_WITHOUT_ABORT);
-}
-
TEST(logging, PLOG_FATAL_WITHOUT_ABORT_enabled) {
ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(FATAL_WITHOUT_ABORT));
}
@@ -619,21 +603,6 @@
LOG(ERROR) << "foobar";
}
-TEST(logging, SetDefaultTag) {
- constexpr const char* expected_tag = "test_tag";
- constexpr const char* expected_msg = "foobar";
- CapturedStderr cap;
- {
- std::string old_default_tag = android::base::GetDefaultTag();
- android::base::SetDefaultTag(expected_tag);
- android::base::ScopedLogSeverity sls(android::base::LogSeverity::INFO);
- LOG(INFO) << expected_msg;
- android::base::SetDefaultTag(old_default_tag);
- }
- ASSERT_NO_FATAL_FAILURE(
- CheckMessage(cap, android::base::LogSeverity::INFO, expected_msg, expected_tag));
-}
-
TEST(logging, StdioLogger) {
CapturedStderr cap_err;
CapturedStdout cap_out;
diff --git a/bootstat/boot_reason_test.sh b/bootstat/boot_reason_test.sh
index 8979b0c..f379d76 100755
--- a/bootstat/boot_reason_test.sh
+++ b/bootstat/boot_reason_test.sh
@@ -636,7 +636,7 @@
rm -r ${ANDROID_PRODUCT_OUT}/obj/ETC/system_build_prop_intermediates ||
true
pushd ${ANDROID_BUILD_TOP} >&2
- make -j50 >&2
+ build/soong/soong_ui.bash --make-mode >&2
if [ ${?} != 0 ]; then
popd >&2
return 1
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 780e48d..c8df3e3 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -355,7 +355,3 @@
init_rc: ["tombstoned/tombstoned.rc"],
}
-
-subdirs = [
- "crasher",
-]
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index 6e01289..f8192b5 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -592,19 +592,20 @@
g_callbacks = *callbacks;
}
- void* thread_stack_allocation =
- mmap(nullptr, PAGE_SIZE * 3, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+ size_t thread_stack_pages = 8;
+ void* thread_stack_allocation = mmap(nullptr, PAGE_SIZE * (thread_stack_pages + 2), PROT_NONE,
+ MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
if (thread_stack_allocation == MAP_FAILED) {
fatal_errno("failed to allocate debuggerd thread stack");
}
char* stack = static_cast<char*>(thread_stack_allocation) + PAGE_SIZE;
- if (mprotect(stack, PAGE_SIZE, PROT_READ | PROT_WRITE) != 0) {
+ if (mprotect(stack, PAGE_SIZE * thread_stack_pages, PROT_READ | PROT_WRITE) != 0) {
fatal_errno("failed to mprotect debuggerd thread stack");
}
// Stack grows negatively, set it to the last byte in the page...
- stack = (stack + PAGE_SIZE - 1);
+ stack = (stack + thread_stack_pages * PAGE_SIZE - 1);
// and align it.
stack -= 15;
pseudothread_stack = stack;
diff --git a/debuggerd/libdebuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp
index 9dea7ac..b33adf3 100644
--- a/debuggerd/libdebuggerd/test/tombstone_test.cpp
+++ b/debuggerd/libdebuggerd/test/tombstone_test.cpp
@@ -359,467 +359,3 @@
dump_timestamp(&log_, 0);
ASSERT_STREQ("Timestamp: 1970-01-01 00:00:00+0000\n", amfd_data_.c_str());
}
-
-class MemoryPattern : public unwindstack::Memory {
- public:
- MemoryPattern() = default;
- virtual ~MemoryPattern() = default;
-
- size_t Read(uint64_t, void* dst, size_t size) override {
- uint8_t* data = reinterpret_cast<uint8_t*>(dst);
- for (size_t i = 0; i < size; i++) {
- data[i] = (i % 0xff);
- }
- return size;
- }
-};
-
-TEST_F(TombstoneTest, dump_stack_single_frame) {
- std::vector<unwindstack::FrameData> frames;
- unwindstack::Maps maps;
- MemoryPattern memory;
-
- frames.push_back(
- unwindstack::FrameData{.num = 0, .rel_pc = 0x1000, .pc = 0x301000, .sp = 0x2000});
- dump_stack(&log_, frames, &maps, &memory);
-
- std::string contents;
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &contents));
-
- std::string expected =
-#if defined(__LP64__)
- " 0000000000001f80 0706050403020100\n"
- " 0000000000001f88 0f0e0d0c0b0a0908\n"
- " 0000000000001f90 1716151413121110\n"
- " 0000000000001f98 1f1e1d1c1b1a1918\n"
- " 0000000000001fa0 2726252423222120\n"
- " 0000000000001fa8 2f2e2d2c2b2a2928\n"
- " 0000000000001fb0 3736353433323130\n"
- " 0000000000001fb8 3f3e3d3c3b3a3938\n"
- " 0000000000001fc0 4746454443424140\n"
- " 0000000000001fc8 4f4e4d4c4b4a4948\n"
- " 0000000000001fd0 5756555453525150\n"
- " 0000000000001fd8 5f5e5d5c5b5a5958\n"
- " 0000000000001fe0 6766656463626160\n"
- " 0000000000001fe8 6f6e6d6c6b6a6968\n"
- " 0000000000001ff0 7776757473727170\n"
- " 0000000000001ff8 7f7e7d7c7b7a7978\n"
- " #00 0000000000002000 0706050403020100\n"
- " 0000000000002008 0f0e0d0c0b0a0908\n"
- " 0000000000002010 1716151413121110\n"
- " 0000000000002018 1f1e1d1c1b1a1918\n"
- " 0000000000002020 2726252423222120\n"
- " 0000000000002028 2f2e2d2c2b2a2928\n"
- " 0000000000002030 3736353433323130\n"
- " 0000000000002038 3f3e3d3c3b3a3938\n"
- " 0000000000002040 4746454443424140\n"
- " 0000000000002048 4f4e4d4c4b4a4948\n"
- " 0000000000002050 5756555453525150\n"
- " 0000000000002058 5f5e5d5c5b5a5958\n"
- " 0000000000002060 6766656463626160\n"
- " 0000000000002068 6f6e6d6c6b6a6968\n"
- " 0000000000002070 7776757473727170\n"
- " 0000000000002078 7f7e7d7c7b7a7978\n";
-#else
- " 00001fc0 03020100\n"
- " 00001fc4 07060504\n"
- " 00001fc8 0b0a0908\n"
- " 00001fcc 0f0e0d0c\n"
- " 00001fd0 13121110\n"
- " 00001fd4 17161514\n"
- " 00001fd8 1b1a1918\n"
- " 00001fdc 1f1e1d1c\n"
- " 00001fe0 23222120\n"
- " 00001fe4 27262524\n"
- " 00001fe8 2b2a2928\n"
- " 00001fec 2f2e2d2c\n"
- " 00001ff0 33323130\n"
- " 00001ff4 37363534\n"
- " 00001ff8 3b3a3938\n"
- " 00001ffc 3f3e3d3c\n"
- " #00 00002000 03020100\n"
- " 00002004 07060504\n"
- " 00002008 0b0a0908\n"
- " 0000200c 0f0e0d0c\n"
- " 00002010 13121110\n"
- " 00002014 17161514\n"
- " 00002018 1b1a1918\n"
- " 0000201c 1f1e1d1c\n"
- " 00002020 23222120\n"
- " 00002024 27262524\n"
- " 00002028 2b2a2928\n"
- " 0000202c 2f2e2d2c\n"
- " 00002030 33323130\n"
- " 00002034 37363534\n"
- " 00002038 3b3a3938\n"
- " 0000203c 3f3e3d3c\n";
-#endif
- EXPECT_EQ(expected, contents);
-}
-
-TEST_F(TombstoneTest, dump_stack_multiple_frames_same_sp) {
- std::vector<unwindstack::FrameData> frames;
- unwindstack::Maps maps;
- MemoryPattern memory;
-
- frames.push_back(
- unwindstack::FrameData{.num = 0, .rel_pc = 0x1000, .pc = 0x301000, .sp = 0x2000});
- frames.push_back(
- unwindstack::FrameData{.num = 0, .rel_pc = 0x1400, .pc = 0x301400, .sp = 0x2000});
- dump_stack(&log_, frames, &maps, &memory);
-
- std::string contents;
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &contents));
-
- std::string expected =
-#if defined(__LP64__)
- " 0000000000001f80 0706050403020100\n"
- " 0000000000001f88 0f0e0d0c0b0a0908\n"
- " 0000000000001f90 1716151413121110\n"
- " 0000000000001f98 1f1e1d1c1b1a1918\n"
- " 0000000000001fa0 2726252423222120\n"
- " 0000000000001fa8 2f2e2d2c2b2a2928\n"
- " 0000000000001fb0 3736353433323130\n"
- " 0000000000001fb8 3f3e3d3c3b3a3938\n"
- " 0000000000001fc0 4746454443424140\n"
- " 0000000000001fc8 4f4e4d4c4b4a4948\n"
- " 0000000000001fd0 5756555453525150\n"
- " 0000000000001fd8 5f5e5d5c5b5a5958\n"
- " 0000000000001fe0 6766656463626160\n"
- " 0000000000001fe8 6f6e6d6c6b6a6968\n"
- " 0000000000001ff0 7776757473727170\n"
- " 0000000000001ff8 7f7e7d7c7b7a7978\n"
- " #00 0000000000002000 0706050403020100\n"
- " ................ ................\n"
- " #01 0000000000002000 0706050403020100\n"
- " 0000000000002008 0f0e0d0c0b0a0908\n"
- " 0000000000002010 1716151413121110\n"
- " 0000000000002018 1f1e1d1c1b1a1918\n"
- " 0000000000002020 2726252423222120\n"
- " 0000000000002028 2f2e2d2c2b2a2928\n"
- " 0000000000002030 3736353433323130\n"
- " 0000000000002038 3f3e3d3c3b3a3938\n"
- " 0000000000002040 4746454443424140\n"
- " 0000000000002048 4f4e4d4c4b4a4948\n"
- " 0000000000002050 5756555453525150\n"
- " 0000000000002058 5f5e5d5c5b5a5958\n"
- " 0000000000002060 6766656463626160\n"
- " 0000000000002068 6f6e6d6c6b6a6968\n"
- " 0000000000002070 7776757473727170\n"
- " 0000000000002078 7f7e7d7c7b7a7978\n";
-#else
- " 00001fc0 03020100\n"
- " 00001fc4 07060504\n"
- " 00001fc8 0b0a0908\n"
- " 00001fcc 0f0e0d0c\n"
- " 00001fd0 13121110\n"
- " 00001fd4 17161514\n"
- " 00001fd8 1b1a1918\n"
- " 00001fdc 1f1e1d1c\n"
- " 00001fe0 23222120\n"
- " 00001fe4 27262524\n"
- " 00001fe8 2b2a2928\n"
- " 00001fec 2f2e2d2c\n"
- " 00001ff0 33323130\n"
- " 00001ff4 37363534\n"
- " 00001ff8 3b3a3938\n"
- " 00001ffc 3f3e3d3c\n"
- " #00 00002000 03020100\n"
- " ........ ........\n"
- " #01 00002000 03020100\n"
- " 00002004 07060504\n"
- " 00002008 0b0a0908\n"
- " 0000200c 0f0e0d0c\n"
- " 00002010 13121110\n"
- " 00002014 17161514\n"
- " 00002018 1b1a1918\n"
- " 0000201c 1f1e1d1c\n"
- " 00002020 23222120\n"
- " 00002024 27262524\n"
- " 00002028 2b2a2928\n"
- " 0000202c 2f2e2d2c\n"
- " 00002030 33323130\n"
- " 00002034 37363534\n"
- " 00002038 3b3a3938\n"
- " 0000203c 3f3e3d3c\n";
-#endif
- EXPECT_EQ(expected, contents);
-}
-
-TEST_F(TombstoneTest, dump_stack_multiple_frames) {
- std::vector<unwindstack::FrameData> frames;
- unwindstack::Maps maps;
- MemoryPattern memory;
-
- frames.push_back(
- unwindstack::FrameData{.num = 0, .rel_pc = 0x1000, .pc = 0x301000, .sp = 0x2000});
- frames.push_back(
- unwindstack::FrameData{.num = 0, .rel_pc = 0x1400, .pc = 0x301400, .sp = 0x2010});
- frames.push_back(
- unwindstack::FrameData{.num = 0, .rel_pc = 0x1400, .pc = 0x301400, .sp = 0x2100});
- dump_stack(&log_, frames, &maps, &memory);
-
- std::string contents;
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &contents));
-
- std::string expected =
-#if defined(__LP64__)
- " 0000000000001f80 0706050403020100\n"
- " 0000000000001f88 0f0e0d0c0b0a0908\n"
- " 0000000000001f90 1716151413121110\n"
- " 0000000000001f98 1f1e1d1c1b1a1918\n"
- " 0000000000001fa0 2726252423222120\n"
- " 0000000000001fa8 2f2e2d2c2b2a2928\n"
- " 0000000000001fb0 3736353433323130\n"
- " 0000000000001fb8 3f3e3d3c3b3a3938\n"
- " 0000000000001fc0 4746454443424140\n"
- " 0000000000001fc8 4f4e4d4c4b4a4948\n"
- " 0000000000001fd0 5756555453525150\n"
- " 0000000000001fd8 5f5e5d5c5b5a5958\n"
- " 0000000000001fe0 6766656463626160\n"
- " 0000000000001fe8 6f6e6d6c6b6a6968\n"
- " 0000000000001ff0 7776757473727170\n"
- " 0000000000001ff8 7f7e7d7c7b7a7978\n"
- " #00 0000000000002000 0706050403020100\n"
- " 0000000000002008 0f0e0d0c0b0a0908\n"
- " #01 0000000000002010 0706050403020100\n"
- " 0000000000002018 0f0e0d0c0b0a0908\n"
- " 0000000000002020 1716151413121110\n"
- " 0000000000002028 1f1e1d1c1b1a1918\n"
- " 0000000000002030 2726252423222120\n"
- " 0000000000002038 2f2e2d2c2b2a2928\n"
- " 0000000000002040 3736353433323130\n"
- " 0000000000002048 3f3e3d3c3b3a3938\n"
- " 0000000000002050 4746454443424140\n"
- " 0000000000002058 4f4e4d4c4b4a4948\n"
- " 0000000000002060 5756555453525150\n"
- " 0000000000002068 5f5e5d5c5b5a5958\n"
- " 0000000000002070 6766656463626160\n"
- " 0000000000002078 6f6e6d6c6b6a6968\n"
- " 0000000000002080 7776757473727170\n"
- " 0000000000002088 7f7e7d7c7b7a7978\n"
- " ................ ................\n"
- " #02 0000000000002100 0706050403020100\n"
- " 0000000000002108 0f0e0d0c0b0a0908\n"
- " 0000000000002110 1716151413121110\n"
- " 0000000000002118 1f1e1d1c1b1a1918\n"
- " 0000000000002120 2726252423222120\n"
- " 0000000000002128 2f2e2d2c2b2a2928\n"
- " 0000000000002130 3736353433323130\n"
- " 0000000000002138 3f3e3d3c3b3a3938\n"
- " 0000000000002140 4746454443424140\n"
- " 0000000000002148 4f4e4d4c4b4a4948\n"
- " 0000000000002150 5756555453525150\n"
- " 0000000000002158 5f5e5d5c5b5a5958\n"
- " 0000000000002160 6766656463626160\n"
- " 0000000000002168 6f6e6d6c6b6a6968\n"
- " 0000000000002170 7776757473727170\n"
- " 0000000000002178 7f7e7d7c7b7a7978\n";
-#else
- " 00001fc0 03020100\n"
- " 00001fc4 07060504\n"
- " 00001fc8 0b0a0908\n"
- " 00001fcc 0f0e0d0c\n"
- " 00001fd0 13121110\n"
- " 00001fd4 17161514\n"
- " 00001fd8 1b1a1918\n"
- " 00001fdc 1f1e1d1c\n"
- " 00001fe0 23222120\n"
- " 00001fe4 27262524\n"
- " 00001fe8 2b2a2928\n"
- " 00001fec 2f2e2d2c\n"
- " 00001ff0 33323130\n"
- " 00001ff4 37363534\n"
- " 00001ff8 3b3a3938\n"
- " 00001ffc 3f3e3d3c\n"
- " #00 00002000 03020100\n"
- " 00002004 07060504\n"
- " 00002008 0b0a0908\n"
- " 0000200c 0f0e0d0c\n"
- " #01 00002010 03020100\n"
- " 00002014 07060504\n"
- " 00002018 0b0a0908\n"
- " 0000201c 0f0e0d0c\n"
- " 00002020 13121110\n"
- " 00002024 17161514\n"
- " 00002028 1b1a1918\n"
- " 0000202c 1f1e1d1c\n"
- " 00002030 23222120\n"
- " 00002034 27262524\n"
- " 00002038 2b2a2928\n"
- " 0000203c 2f2e2d2c\n"
- " 00002040 33323130\n"
- " 00002044 37363534\n"
- " 00002048 3b3a3938\n"
- " 0000204c 3f3e3d3c\n"
- " ........ ........\n"
- " #02 00002100 03020100\n"
- " 00002104 07060504\n"
- " 00002108 0b0a0908\n"
- " 0000210c 0f0e0d0c\n"
- " 00002110 13121110\n"
- " 00002114 17161514\n"
- " 00002118 1b1a1918\n"
- " 0000211c 1f1e1d1c\n"
- " 00002120 23222120\n"
- " 00002124 27262524\n"
- " 00002128 2b2a2928\n"
- " 0000212c 2f2e2d2c\n"
- " 00002130 33323130\n"
- " 00002134 37363534\n"
- " 00002138 3b3a3938\n"
- " 0000213c 3f3e3d3c\n";
-#endif
- EXPECT_EQ(expected, contents);
-}
-
-TEST_F(TombstoneTest, dump_stack_multiple_frames_disjoint_frames) {
- std::vector<unwindstack::FrameData> frames;
- unwindstack::Maps maps;
- MemoryPattern memory;
-
- frames.push_back(
- unwindstack::FrameData{.num = 0, .rel_pc = 0x1000, .pc = 0x301000, .sp = 0x2000});
- frames.push_back(
- unwindstack::FrameData{.num = 0, .rel_pc = 0x1400, .pc = 0x301400, .sp = 0x2010});
- frames.push_back(
- unwindstack::FrameData{.num = 0, .rel_pc = 0x1400, .pc = 0x301400, .sp = 0x1000});
- frames.push_back(
- unwindstack::FrameData{.num = 0, .rel_pc = 0x1400, .pc = 0x301400, .sp = 0x1030});
- dump_stack(&log_, frames, &maps, &memory);
-
- std::string contents;
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &contents));
-
- std::string expected =
-#if defined(__LP64__)
- " 0000000000001f80 0706050403020100\n"
- " 0000000000001f88 0f0e0d0c0b0a0908\n"
- " 0000000000001f90 1716151413121110\n"
- " 0000000000001f98 1f1e1d1c1b1a1918\n"
- " 0000000000001fa0 2726252423222120\n"
- " 0000000000001fa8 2f2e2d2c2b2a2928\n"
- " 0000000000001fb0 3736353433323130\n"
- " 0000000000001fb8 3f3e3d3c3b3a3938\n"
- " 0000000000001fc0 4746454443424140\n"
- " 0000000000001fc8 4f4e4d4c4b4a4948\n"
- " 0000000000001fd0 5756555453525150\n"
- " 0000000000001fd8 5f5e5d5c5b5a5958\n"
- " 0000000000001fe0 6766656463626160\n"
- " 0000000000001fe8 6f6e6d6c6b6a6968\n"
- " 0000000000001ff0 7776757473727170\n"
- " 0000000000001ff8 7f7e7d7c7b7a7978\n"
- " #00 0000000000002000 0706050403020100\n"
- " 0000000000002008 0f0e0d0c0b0a0908\n"
- " #01 0000000000002010 0706050403020100\n"
- " 0000000000002018 0f0e0d0c0b0a0908\n"
- " 0000000000002020 1716151413121110\n"
- " 0000000000002028 1f1e1d1c1b1a1918\n"
- " 0000000000002030 2726252423222120\n"
- " 0000000000002038 2f2e2d2c2b2a2928\n"
- " 0000000000002040 3736353433323130\n"
- " 0000000000002048 3f3e3d3c3b3a3938\n"
- " 0000000000002050 4746454443424140\n"
- " 0000000000002058 4f4e4d4c4b4a4948\n"
- " 0000000000002060 5756555453525150\n"
- " 0000000000002068 5f5e5d5c5b5a5958\n"
- " 0000000000002070 6766656463626160\n"
- " 0000000000002078 6f6e6d6c6b6a6968\n"
- " 0000000000002080 7776757473727170\n"
- " 0000000000002088 7f7e7d7c7b7a7978\n"
- " ................ ................\n"
- " #02 0000000000001000 0706050403020100\n"
- " 0000000000001008 0f0e0d0c0b0a0908\n"
- " 0000000000001010 1716151413121110\n"
- " 0000000000001018 1f1e1d1c1b1a1918\n"
- " 0000000000001020 2726252423222120\n"
- " 0000000000001028 2f2e2d2c2b2a2928\n"
- " #03 0000000000001030 0706050403020100\n"
- " 0000000000001038 0f0e0d0c0b0a0908\n"
- " 0000000000001040 1716151413121110\n"
- " 0000000000001048 1f1e1d1c1b1a1918\n"
- " 0000000000001050 2726252423222120\n"
- " 0000000000001058 2f2e2d2c2b2a2928\n"
- " 0000000000001060 3736353433323130\n"
- " 0000000000001068 3f3e3d3c3b3a3938\n"
- " 0000000000001070 4746454443424140\n"
- " 0000000000001078 4f4e4d4c4b4a4948\n"
- " 0000000000001080 5756555453525150\n"
- " 0000000000001088 5f5e5d5c5b5a5958\n"
- " 0000000000001090 6766656463626160\n"
- " 0000000000001098 6f6e6d6c6b6a6968\n"
- " 00000000000010a0 7776757473727170\n"
- " 00000000000010a8 7f7e7d7c7b7a7978\n";
-#else
- " 00001fc0 03020100\n"
- " 00001fc4 07060504\n"
- " 00001fc8 0b0a0908\n"
- " 00001fcc 0f0e0d0c\n"
- " 00001fd0 13121110\n"
- " 00001fd4 17161514\n"
- " 00001fd8 1b1a1918\n"
- " 00001fdc 1f1e1d1c\n"
- " 00001fe0 23222120\n"
- " 00001fe4 27262524\n"
- " 00001fe8 2b2a2928\n"
- " 00001fec 2f2e2d2c\n"
- " 00001ff0 33323130\n"
- " 00001ff4 37363534\n"
- " 00001ff8 3b3a3938\n"
- " 00001ffc 3f3e3d3c\n"
- " #00 00002000 03020100\n"
- " 00002004 07060504\n"
- " 00002008 0b0a0908\n"
- " 0000200c 0f0e0d0c\n"
- " #01 00002010 03020100\n"
- " 00002014 07060504\n"
- " 00002018 0b0a0908\n"
- " 0000201c 0f0e0d0c\n"
- " 00002020 13121110\n"
- " 00002024 17161514\n"
- " 00002028 1b1a1918\n"
- " 0000202c 1f1e1d1c\n"
- " 00002030 23222120\n"
- " 00002034 27262524\n"
- " 00002038 2b2a2928\n"
- " 0000203c 2f2e2d2c\n"
- " 00002040 33323130\n"
- " 00002044 37363534\n"
- " 00002048 3b3a3938\n"
- " 0000204c 3f3e3d3c\n"
- " ........ ........\n"
- " #02 00001000 03020100\n"
- " 00001004 07060504\n"
- " 00001008 0b0a0908\n"
- " 0000100c 0f0e0d0c\n"
- " 00001010 13121110\n"
- " 00001014 17161514\n"
- " 00001018 1b1a1918\n"
- " 0000101c 1f1e1d1c\n"
- " 00001020 23222120\n"
- " 00001024 27262524\n"
- " 00001028 2b2a2928\n"
- " 0000102c 2f2e2d2c\n"
- " #03 00001030 03020100\n"
- " 00001034 07060504\n"
- " 00001038 0b0a0908\n"
- " 0000103c 0f0e0d0c\n"
- " 00001040 13121110\n"
- " 00001044 17161514\n"
- " 00001048 1b1a1918\n"
- " 0000104c 1f1e1d1c\n"
- " 00001050 23222120\n"
- " 00001054 27262524\n"
- " 00001058 2b2a2928\n"
- " 0000105c 2f2e2d2c\n"
- " 00001060 33323130\n"
- " 00001064 37363534\n"
- " 00001068 3b3a3938\n"
- " 0000106c 3f3e3d3c\n";
-#endif
- EXPECT_EQ(expected, contents);
-}
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index b64e260..4e7f35c 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -188,106 +188,6 @@
_LOG(log, logtype::HEADER, "uid: %d\n", thread_info.uid);
}
-static void dump_stack_segment(log_t* log, unwindstack::Maps* maps, unwindstack::Memory* memory,
- uint64_t* sp, size_t words, int label) {
- // Read the data all at once.
- word_t stack_data[words];
-
- // TODO: Do we need to word align this for crashes caused by a misaligned sp?
- // The process_vm_readv implementation of Memory should handle this appropriately?
- size_t bytes_read = memory->Read(*sp, stack_data, sizeof(word_t) * words);
- words = bytes_read / sizeof(word_t);
- std::string line;
- for (size_t i = 0; i < words; i++) {
- line = " ";
- if (i == 0 && label >= 0) {
- // Print the label once.
- line += StringPrintf("#%02d ", label);
- } else {
- line += " ";
- }
- line += StringPrintf("%" PRIPTR " %" PRIPTR, *sp, static_cast<uint64_t>(stack_data[i]));
-
- unwindstack::MapInfo* map_info = maps->Find(stack_data[i]);
- if (map_info != nullptr && !map_info->name.empty()) {
- line += " " + map_info->name;
- std::string func_name;
- uint64_t func_offset = 0;
- if (map_info->GetFunctionName(stack_data[i], &func_name, &func_offset)) {
- line += " (" + func_name;
- if (func_offset) {
- line += StringPrintf("+%" PRIu64, func_offset);
- }
- line += ')';
- }
- }
- _LOG(log, logtype::STACK, "%s\n", line.c_str());
-
- *sp += sizeof(word_t);
- }
-}
-
-static void dump_stack(log_t* log, const std::vector<unwindstack::FrameData>& frames,
- unwindstack::Maps* maps, unwindstack::Memory* memory) {
- size_t first = 0, last;
- for (size_t i = 0; i < frames.size(); i++) {
- if (frames[i].sp) {
- if (!first) {
- first = i+1;
- }
- last = i;
- }
- }
-
- if (!first) {
- return;
- }
- first--;
-
- // Dump a few words before the first frame.
- uint64_t sp = frames[first].sp - STACK_WORDS * sizeof(word_t);
- dump_stack_segment(log, maps, memory, &sp, STACK_WORDS, -1);
-
-#if defined(__LP64__)
- static constexpr const char delimiter[] = " ................ ................\n";
-#else
- static constexpr const char delimiter[] = " ........ ........\n";
-#endif
-
- // Dump a few words from all successive frames.
- for (size_t i = first; i <= last; i++) {
- auto* frame = &frames[i];
- if (sp != frame->sp) {
- _LOG(log, logtype::STACK, delimiter);
- sp = frame->sp;
- }
- if (i != last) {
- // Print stack data up to the stack from the next frame.
- size_t words;
- uint64_t next_sp = frames[i + 1].sp;
- if (next_sp < sp) {
- // The next frame is probably using a completely different stack,
- // so dump the max from this stack.
- words = STACK_WORDS;
- } else {
- words = (next_sp - sp) / sizeof(word_t);
- if (words == 0) {
- // The sp is the same as the next frame, print at least
- // one line for this frame.
- words = 1;
- } else if (words > STACK_WORDS) {
- words = STACK_WORDS;
- }
- }
- dump_stack_segment(log, maps, memory, &sp, words, i);
- } else {
- // Print some number of words past the last stack frame since we
- // don't know how large the stack is.
- dump_stack_segment(log, maps, memory, &sp, STACK_WORDS, i);
- }
- }
-}
-
static std::string get_addr_string(uint64_t addr) {
std::string addr_str;
#if defined(__LP64__)
@@ -499,9 +399,6 @@
} else {
_LOG(log, logtype::BACKTRACE, "\nbacktrace:\n");
log_backtrace(log, unwinder, " ");
-
- _LOG(log, logtype::STACK, "\nstack:\n");
- dump_stack(log, unwinder->frames(), unwinder->GetMaps(), unwinder->GetProcessMemory().get());
}
if (primary_thread) {
diff --git a/deprecated-adf/Android.bp b/deprecated-adf/Android.bp
deleted file mode 100644
index b44c296..0000000
--- a/deprecated-adf/Android.bp
+++ /dev/null
@@ -1 +0,0 @@
-subdirs = ["*"]
diff --git a/deprecated-adf/libadf/Android.bp b/deprecated-adf/libadf/Android.bp
index 49e3721..70f0a3b 100644
--- a/deprecated-adf/libadf/Android.bp
+++ b/deprecated-adf/libadf/Android.bp
@@ -24,5 +24,3 @@
local_include_dirs: ["include"],
export_include_dirs: ["include"],
}
-
-subdirs = ["tests"]
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index 17ec392..fd009e7 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -18,9 +18,9 @@
# Package fastboot-related executables.
#
-my_dist_files := $(HOST_OUT_EXECUTABLES)/mke2fs
-my_dist_files += $(HOST_OUT_EXECUTABLES)/e2fsdroid
-my_dist_files += $(HOST_OUT_EXECUTABLES)/make_f2fs
-my_dist_files += $(HOST_OUT_EXECUTABLES)/sload_f2fs
+my_dist_files := $(SOONG_HOST_OUT_EXECUTABLES)/mke2fs
+my_dist_files += $(SOONG_HOST_OUT_EXECUTABLES)/e2fsdroid
+my_dist_files += $(SOONG_HOST_OUT_EXECUTABLES)/make_f2fs
+my_dist_files += $(SOONG_HOST_OUT_EXECUTABLES)/sload_f2fs
$(call dist-for-goals,dist_files sdk win_sdk,$(my_dist_files))
my_dist_files :=
diff --git a/fastboot/constants.h b/fastboot/constants.h
index 5a554a0..aefd448 100644
--- a/fastboot/constants.h
+++ b/fastboot/constants.h
@@ -47,6 +47,8 @@
#define FB_VAR_VERSION "version"
#define FB_VAR_VERSION_BOOTLOADER "version-bootloader"
#define FB_VAR_VERSION_BASEBAND "version-baseband"
+#define FB_VAR_VERSION_OS "version-os"
+#define FB_VAR_VERSION_VNDK "version-vndk"
#define FB_VAR_PRODUCT "product"
#define FB_VAR_SERIALNO "serialno"
#define FB_VAR_SECURE "secure"
@@ -69,3 +71,9 @@
#define FB_VAR_SUPER_PARTITION_NAME "super-partition-name"
#define FB_VAR_SNAPSHOT_UPDATE_STATUS "snapshot-update-status"
#define FB_VAR_CPU_ABI "cpu-abi"
+#define FB_VAR_SYSTEM_FINGERPRINT "system-fingerprint"
+#define FB_VAR_VENDOR_FINGERPRINT "vendor-fingerprint"
+#define FB_VAR_DYNAMIC_PARTITION "dynamic-partition"
+#define FB_VAR_FIRST_API_LEVEL "first-api-level"
+#define FB_VAR_SECURITY_PATCH_LEVEL "security-patch-level"
+#define FB_VAR_TREBLE_ENABLED "treble-enabled"
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index 1a745ab..2c9dec9 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -31,7 +31,6 @@
#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>
@@ -107,6 +106,8 @@
{FB_VAR_VERSION, {GetVersion, nullptr}},
{FB_VAR_VERSION_BOOTLOADER, {GetBootloaderVersion, nullptr}},
{FB_VAR_VERSION_BASEBAND, {GetBasebandVersion, nullptr}},
+ {FB_VAR_VERSION_OS, {GetOsVersion, nullptr}},
+ {FB_VAR_VERSION_VNDK, {GetVndkVersion, nullptr}},
{FB_VAR_PRODUCT, {GetProduct, nullptr}},
{FB_VAR_SERIALNO, {GetSerial, nullptr}},
{FB_VAR_VARIANT, {GetVariant, nullptr}},
@@ -128,7 +129,13 @@
{FB_VAR_HW_REVISION, {GetHardwareRevision, nullptr}},
{FB_VAR_SUPER_PARTITION_NAME, {GetSuperPartitionName, nullptr}},
{FB_VAR_SNAPSHOT_UPDATE_STATUS, {GetSnapshotUpdateStatus, nullptr}},
- {FB_VAR_CPU_ABI, {GetCpuAbi, nullptr}}};
+ {FB_VAR_CPU_ABI, {GetCpuAbi, nullptr}},
+ {FB_VAR_SYSTEM_FINGERPRINT, {GetSystemFingerprint, nullptr}},
+ {FB_VAR_VENDOR_FINGERPRINT, {GetVendorFingerprint, nullptr}},
+ {FB_VAR_DYNAMIC_PARTITION, {GetDynamicPartition, nullptr}},
+ {FB_VAR_FIRST_API_LEVEL, {GetFirstApiLevel, nullptr}},
+ {FB_VAR_SECURITY_PATCH_LEVEL, {GetSecurityPatchLevel, nullptr}},
+ {FB_VAR_TREBLE_ENABLED, {GetTrebleEnabled, nullptr}}};
if (args.size() < 2) {
return device->WriteFail("Missing argument");
@@ -550,42 +557,6 @@
return UpdateSuper(device, args[1], wipe);
}
-class AutoMountMetadata {
- public:
- AutoMountMetadata() {
- android::fs_mgr::Fstab proc_mounts;
- if (!ReadFstabFromFile("/proc/mounts", &proc_mounts)) {
- LOG(ERROR) << "Could not read /proc/mounts";
- return;
- }
-
- auto iter = std::find_if(proc_mounts.begin(), proc_mounts.end(),
- [](const auto& entry) { return entry.mount_point == "/metadata"; });
- if (iter != proc_mounts.end()) {
- mounted_ = true;
- return;
- }
-
- if (!ReadDefaultFstab(&fstab_)) {
- LOG(ERROR) << "Could not read default fstab";
- return;
- }
- mounted_ = EnsurePathMounted(&fstab_, "/metadata");
- should_unmount_ = true;
- }
- ~AutoMountMetadata() {
- if (mounted_ && should_unmount_) {
- EnsurePathUnmounted(&fstab_, "/metadata");
- }
- }
- explicit operator bool() const { return mounted_; }
-
- private:
- android::fs_mgr::Fstab fstab_;
- bool mounted_ = false;
- bool should_unmount_ = false;
-};
-
bool GsiHandler(FastbootDevice* device, const std::vector<std::string>& args) {
if (args.size() != 2) {
return device->WriteFail("Invalid arguments");
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index 102ebdb..7e7e507 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -21,6 +21,7 @@
#include <algorithm>
#include <memory>
+#include <optional>
#include <set>
#include <string>
@@ -56,6 +57,7 @@
Fstab fstab;
ReadDefaultFstab(&fstab);
+ std::optional<AutoMountMetadata> mount_metadata;
for (const auto& entry : fstab) {
auto partition = android::base::Basename(entry.mount_point);
if ("/" == entry.mount_point) {
@@ -63,6 +65,7 @@
}
if ((partition + device->GetCurrentSlot()) == partition_name) {
+ mount_metadata.emplace();
fs_mgr_overlayfs_teardown(entry.mount_point.c_str());
}
}
diff --git a/fastboot/device/usb_client.cpp b/fastboot/device/usb_client.cpp
index 5066046..9c80765 100644
--- a/fastboot/device/usb_client.cpp
+++ b/fastboot/device/usb_client.cpp
@@ -297,3 +297,7 @@
CloseFunctionFs(handle_.get());
return 0;
}
+
+int ClientUsbTransport::Reset() {
+ return 0;
+}
diff --git a/fastboot/device/usb_client.h b/fastboot/device/usb_client.h
index 3694f9a..e6a1a8b 100644
--- a/fastboot/device/usb_client.h
+++ b/fastboot/device/usb_client.h
@@ -29,6 +29,7 @@
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:
std::unique_ptr<usb_handle> handle_;
diff --git a/fastboot/device/utility.cpp b/fastboot/device/utility.cpp
index b3f2d5f..7c6ac89 100644
--- a/fastboot/device/utility.cpp
+++ b/fastboot/device/utility.cpp
@@ -26,6 +26,7 @@
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <fs_mgr.h>
+#include <fs_mgr/roots.h>
#include <fs_mgr_dm_linear.h>
#include <liblp/builder.h>
#include <liblp/liblp.h>
@@ -240,3 +241,29 @@
}
return current_slot_suffix;
}
+
+AutoMountMetadata::AutoMountMetadata() {
+ android::fs_mgr::Fstab proc_mounts;
+ if (!ReadFstabFromFile("/proc/mounts", &proc_mounts)) {
+ LOG(ERROR) << "Could not read /proc/mounts";
+ return;
+ }
+
+ if (GetEntryForMountPoint(&proc_mounts, "/metadata")) {
+ mounted_ = true;
+ return;
+ }
+
+ if (!ReadDefaultFstab(&fstab_)) {
+ LOG(ERROR) << "Could not read default fstab";
+ return;
+ }
+ mounted_ = EnsurePathMounted(&fstab_, "/metadata");
+ should_unmount_ = true;
+}
+
+AutoMountMetadata::~AutoMountMetadata() {
+ if (mounted_ && should_unmount_) {
+ EnsurePathUnmounted(&fstab_, "/metadata");
+ }
+}
diff --git a/fastboot/device/utility.h b/fastboot/device/utility.h
index bfeeb74..3b71ef0 100644
--- a/fastboot/device/utility.h
+++ b/fastboot/device/utility.h
@@ -20,6 +20,7 @@
#include <android-base/unique_fd.h>
#include <android/hardware/boot/1.0/IBootControl.h>
+#include <fstab/fstab.h>
#include <liblp/liblp.h>
// Logical partitions are only mapped to a block device as needed, and
@@ -51,6 +52,18 @@
std::function<void()> closer_;
};
+class AutoMountMetadata {
+ public:
+ AutoMountMetadata();
+ ~AutoMountMetadata();
+ explicit operator bool() const { return mounted_; }
+
+ private:
+ android::fs_mgr::Fstab fstab_;
+ bool mounted_ = false;
+ bool should_unmount_ = false;
+};
+
class FastbootDevice;
// On normal devices, the super partition is always named "super". On retrofit
diff --git a/fastboot/device/variables.cpp b/fastboot/device/variables.cpp
index 10eac01..e7d8bc3 100644
--- a/fastboot/device/variables.cpp
+++ b/fastboot/device/variables.cpp
@@ -62,6 +62,18 @@
return true;
}
+bool GetOsVersion(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+ std::string* message) {
+ *message = android::base::GetProperty("ro.build.version.release", "");
+ return true;
+}
+
+bool GetVndkVersion(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+ std::string* message) {
+ *message = android::base::GetProperty("ro.vndk.version", "");
+ return true;
+}
+
bool GetProduct(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
std::string* message) {
*message = android::base::GetProperty("ro.product.device", "");
@@ -458,3 +470,42 @@
*message = android::base::GetProperty("ro.product.cpu.abi", "");
return true;
}
+
+bool GetSystemFingerprint(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+ std::string* message) {
+ *message = android::base::GetProperty("ro.system.build.fingerprint", "");
+ if (message->empty()) {
+ *message = android::base::GetProperty("ro.build.fingerprint", "");
+ }
+ return true;
+}
+
+bool GetVendorFingerprint(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+ std::string* message) {
+ *message = android::base::GetProperty("ro.vendor.build.fingerprint", "");
+ return true;
+}
+
+bool GetDynamicPartition(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+ std::string* message) {
+ *message = android::base::GetProperty("ro.boot.dynamic_partitions", "");
+ return true;
+}
+
+bool GetFirstApiLevel(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+ std::string* message) {
+ *message = android::base::GetProperty("ro.product.first_api_level", "");
+ return true;
+}
+
+bool GetSecurityPatchLevel(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+ std::string* message) {
+ *message = android::base::GetProperty("ro.build.version.security_patch", "");
+ return true;
+}
+
+bool GetTrebleEnabled(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+ std::string* message) {
+ *message = android::base::GetProperty("ro.treble.enabled", "");
+ return true;
+}
diff --git a/fastboot/device/variables.h b/fastboot/device/variables.h
index 90840d6..c11e472 100644
--- a/fastboot/device/variables.h
+++ b/fastboot/device/variables.h
@@ -26,6 +26,10 @@
std::string* message);
bool GetBasebandVersion(FastbootDevice* device, const std::vector<std::string>& args,
std::string* message);
+bool GetOsVersion(FastbootDevice* device, const std::vector<std::string>& args,
+ std::string* message);
+bool GetVndkVersion(FastbootDevice* device, const std::vector<std::string>& args,
+ std::string* message);
bool GetProduct(FastbootDevice* device, const std::vector<std::string>& args, std::string* message);
bool GetSerial(FastbootDevice* device, const std::vector<std::string>& args, std::string* message);
bool GetSecure(FastbootDevice* device, const std::vector<std::string>& args, std::string* message);
@@ -64,6 +68,18 @@
bool GetSnapshotUpdateStatus(FastbootDevice* device, const std::vector<std::string>& args,
std::string* message);
bool GetCpuAbi(FastbootDevice* device, const std::vector<std::string>& args, std::string* message);
+bool GetSystemFingerprint(FastbootDevice* device, const std::vector<std::string>& args,
+ std::string* message);
+bool GetVendorFingerprint(FastbootDevice* device, const std::vector<std::string>& args,
+ std::string* message);
+bool GetDynamicPartition(FastbootDevice* device, const std::vector<std::string>& args,
+ std::string* message);
+bool GetFirstApiLevel(FastbootDevice* device, const std::vector<std::string>& args,
+ std::string* message);
+bool GetSecurityPatchLevel(FastbootDevice* device, const std::vector<std::string>& args,
+ std::string* message);
+bool GetTrebleEnabled(FastbootDevice* device, const std::vector<std::string>& args,
+ std::string* message);
// Helpers for getvar all.
std::vector<std::vector<std::string>> GetAllPartitionArgsWithSlot(FastbootDevice* device);
diff --git a/fastboot/fuzzy_fastboot/Android.bp b/fastboot/fuzzy_fastboot/Android.bp
index 19b33e4..bb54fd9 100644
--- a/fastboot/fuzzy_fastboot/Android.bp
+++ b/fastboot/fuzzy_fastboot/Android.bp
@@ -5,7 +5,7 @@
srcs: [
"main.cpp",
"extensions.cpp",
- "usb_transport_sniffer.cpp",
+ "transport_sniffer.cpp",
"fixtures.cpp",
"test_utils.cpp",
],
@@ -31,6 +31,8 @@
"libext4_utils",
],
+ stl: "libc++_static",
+
// Static libs (libfastboot2) shared library dependencies are not transitively included
// This is needed to avoid link time errors when building for mac
target: {
diff --git a/fastboot/fuzzy_fastboot/fixtures.cpp b/fastboot/fuzzy_fastboot/fixtures.cpp
index bc13a8c..bd76ff4 100644
--- a/fastboot/fuzzy_fastboot/fixtures.cpp
+++ b/fastboot/fuzzy_fastboot/fixtures.cpp
@@ -48,12 +48,13 @@
#include <gtest/gtest.h>
#include "fastboot_driver.h"
+#include "tcp.h"
#include "usb.h"
#include "extensions.h"
#include "fixtures.h"
#include "test_utils.h"
-#include "usb_transport_sniffer.h"
+#include "transport_sniffer.h"
using namespace std::literals::chrono_literals;
@@ -74,7 +75,14 @@
return 0;
}
+bool FastBootTest::IsFastbootOverTcp() {
+ // serial contains ":" is treated as host ip and port number
+ return (device_serial.find(":") != std::string::npos);
+}
+
bool FastBootTest::UsbStillAvailible() {
+ if (IsFastbootOverTcp()) return true;
+
// For some reason someone decided to prefix the path with "usb:"
std::string prefix("usb:");
if (std::equal(prefix.begin(), prefix.end(), device_path.begin())) {
@@ -113,15 +121,19 @@
ASSERT_TRUE(UsbStillAvailible()); // The device disconnected
}
- const auto matcher = [](usb_ifc_info* info) -> int {
- return MatchFastboot(info, device_serial);
- };
- for (int i = 0; i < MAX_USB_TRIES && !transport; i++) {
- std::unique_ptr<UsbTransport> usb(usb_open(matcher, USB_TIMEOUT));
- if (usb)
- transport = std::unique_ptr<UsbTransportSniffer>(
- new UsbTransportSniffer(std::move(usb), serial_port));
- std::this_thread::sleep_for(std::chrono::milliseconds(10));
+ if (IsFastbootOverTcp()) {
+ ConnectTcpFastbootDevice();
+ } else {
+ const auto matcher = [](usb_ifc_info* info) -> int {
+ return MatchFastboot(info, device_serial);
+ };
+ for (int i = 0; i < MAX_USB_TRIES && !transport; i++) {
+ std::unique_ptr<UsbTransport> usb(usb_open(matcher, USB_TIMEOUT));
+ if (usb)
+ transport = std::unique_ptr<TransportSniffer>(
+ new TransportSniffer(std::move(usb), serial_port));
+ std::this_thread::sleep_for(std::chrono::milliseconds(10));
+ }
}
ASSERT_TRUE(transport); // no nullptr
@@ -154,6 +166,8 @@
// TODO, this should eventually be piped to a file instead of stdout
void FastBootTest::TearDownSerial() {
+ if (IsFastbootOverTcp()) return;
+
if (!transport) return;
// One last read from serial
transport->ProcessSerial();
@@ -167,9 +181,34 @@
}
}
+void FastBootTest::ConnectTcpFastbootDevice() {
+ std::size_t found = device_serial.find(":");
+ if (found != std::string::npos) {
+ for (int i = 0; i < MAX_TCP_TRIES && !transport; i++) {
+ std::string error;
+ std::unique_ptr<Transport> tcp(
+ tcp::Connect(device_serial.substr(0, found), tcp::kDefaultPort, &error)
+ .release());
+ if (tcp)
+ transport =
+ std::unique_ptr<TransportSniffer>(new TransportSniffer(std::move(tcp), 0));
+ if (transport != nullptr) break;
+ std::this_thread::sleep_for(std::chrono::milliseconds(10));
+ }
+ }
+}
+
void FastBootTest::ReconnectFastbootDevice() {
fb.reset();
transport.reset();
+
+ if (IsFastbootOverTcp()) {
+ ConnectTcpFastbootDevice();
+ device_path = cb_scratch;
+ fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(transport.get(), {}, true));
+ return;
+ }
+
while (UsbStillAvailible())
;
printf("WAITING FOR DEVICE\n");
@@ -180,8 +219,8 @@
while (!transport) {
std::unique_ptr<UsbTransport> usb(usb_open(matcher, USB_TIMEOUT));
if (usb) {
- transport = std::unique_ptr<UsbTransportSniffer>(
- new UsbTransportSniffer(std::move(usb), serial_port));
+ transport = std::unique_ptr<TransportSniffer>(
+ new TransportSniffer(std::move(usb), serial_port));
}
std::this_thread::sleep_for(1s);
}
diff --git a/fastboot/fuzzy_fastboot/fixtures.h b/fastboot/fuzzy_fastboot/fixtures.h
index c71c897..2468868 100644
--- a/fastboot/fuzzy_fastboot/fixtures.h
+++ b/fastboot/fuzzy_fastboot/fixtures.h
@@ -31,7 +31,7 @@
#include "fastboot_driver.h"
#include "extensions.h"
-#include "usb_transport_sniffer.h"
+#include "transport_sniffer.h"
namespace fastboot {
@@ -45,11 +45,14 @@
static int serial_port;
static std::string device_serial;
static constexpr int MAX_USB_TRIES = 10;
+ static constexpr int MAX_TCP_TRIES = 6000;
static int MatchFastboot(usb_ifc_info* info, const std::string& local_serial = "");
+ static bool IsFastbootOverTcp();
bool UsbStillAvailible();
bool UserSpaceFastboot();
void ReconnectFastbootDevice();
+ void ConnectTcpFastbootDevice();
protected:
RetCode DownloadCommand(uint32_t size, std::string* response = nullptr,
@@ -64,7 +67,7 @@
void TearDownSerial();
void SetLockState(bool unlock, bool assert_change = true);
- std::unique_ptr<UsbTransportSniffer> transport;
+ std::unique_ptr<TransportSniffer> transport;
std::unique_ptr<FastBootDriver> fb;
private:
diff --git a/fastboot/fuzzy_fastboot/main.cpp b/fastboot/fuzzy_fastboot/main.cpp
index a1d69d2..b9784fe 100644
--- a/fastboot/fuzzy_fastboot/main.cpp
+++ b/fastboot/fuzzy_fastboot/main.cpp
@@ -54,7 +54,7 @@
#include "extensions.h"
#include "fixtures.h"
#include "test_utils.h"
-#include "usb_transport_sniffer.h"
+#include "transport_sniffer.h"
namespace fastboot {
@@ -1756,16 +1756,19 @@
}
setbuf(stdout, NULL); // no buffering
- printf("<Waiting for Device>\n");
- const auto matcher = [](usb_ifc_info* info) -> int {
- return fastboot::FastBootTest::MatchFastboot(info, fastboot::FastBootTest::device_serial);
- };
- Transport* transport = nullptr;
- while (!transport) {
- transport = usb_open(matcher);
- std::this_thread::sleep_for(std::chrono::milliseconds(10));
+
+ if (!fastboot::FastBootTest::IsFastbootOverTcp()) {
+ printf("<Waiting for Device>\n");
+ const auto matcher = [](usb_ifc_info* info) -> int {
+ return fastboot::FastBootTest::MatchFastboot(info, fastboot::FastBootTest::device_serial);
+ };
+ Transport* transport = nullptr;
+ while (!transport) {
+ transport = usb_open(matcher);
+ std::this_thread::sleep_for(std::chrono::milliseconds(10));
+ }
+ transport->Close();
}
- transport->Close();
if (args.find("serial_port") != args.end()) {
fastboot::FastBootTest::serial_port = fastboot::ConfigureSerial(args.at("serial_port"));
diff --git a/fastboot/fuzzy_fastboot/usb_transport_sniffer.cpp b/fastboot/fuzzy_fastboot/transport_sniffer.cpp
similarity index 91%
rename from fastboot/fuzzy_fastboot/usb_transport_sniffer.cpp
rename to fastboot/fuzzy_fastboot/transport_sniffer.cpp
index 7c595f4..b55ffd3 100644
--- a/fastboot/fuzzy_fastboot/usb_transport_sniffer.cpp
+++ b/fastboot/fuzzy_fastboot/transport_sniffer.cpp
@@ -1,4 +1,4 @@
-#include "usb_transport_sniffer.h"
+#include "transport_sniffer.h"
#include <android-base/stringprintf.h>
#include <sys/select.h>
#include <sys/time.h>
@@ -8,15 +8,15 @@
namespace fastboot {
-UsbTransportSniffer::UsbTransportSniffer(std::unique_ptr<UsbTransport> transport,
+TransportSniffer::TransportSniffer(std::unique_ptr<Transport> transport,
const int serial_fd)
: transport_(std::move(transport)), serial_fd_(serial_fd) {}
-UsbTransportSniffer::~UsbTransportSniffer() {
+TransportSniffer::~TransportSniffer() {
Close();
}
-ssize_t UsbTransportSniffer::Read(void* data, size_t len) {
+ssize_t TransportSniffer::Read(void* data, size_t len) {
ProcessSerial();
ssize_t ret = transport_->Read(data, len);
@@ -37,7 +37,7 @@
return ret;
}
-ssize_t UsbTransportSniffer::Write(const void* data, size_t len) {
+ssize_t TransportSniffer::Write(const void* data, size_t len) {
ProcessSerial();
size_t ret = transport_->Write(data, len);
@@ -58,11 +58,11 @@
return ret;
}
-int UsbTransportSniffer::Close() {
+int TransportSniffer::Close() {
return transport_->Close();
}
-int UsbTransportSniffer::Reset() {
+int TransportSniffer::Reset() {
ProcessSerial();
int ret = transport_->Reset();
std::vector<char> buf;
@@ -72,7 +72,7 @@
return ret;
}
-const std::vector<UsbTransportSniffer::Event> UsbTransportSniffer::Transfers() {
+const std::vector<TransportSniffer::Event> TransportSniffer::Transfers() {
return transfers_;
}
@@ -81,7 +81,7 @@
* the failure. This method will look through its log of captured events, and
* create a clean printable string of everything that happened.
*/
-std::string UsbTransportSniffer::CreateTrace() {
+std::string TransportSniffer::CreateTrace() {
std::string ret;
const auto no_print = [](char c) -> bool { return !isprint(c); };
@@ -158,7 +158,7 @@
// This is a quick call to flush any UART logs the device might have sent
// to our internal event log. It will wait up to 10ms for data to appear
-void UsbTransportSniffer::ProcessSerial() {
+void TransportSniffer::ProcessSerial() {
if (serial_fd_ <= 0) return;
fd_set set;
diff --git a/fastboot/fuzzy_fastboot/usb_transport_sniffer.h b/fastboot/fuzzy_fastboot/transport_sniffer.h
similarity index 90%
rename from fastboot/fuzzy_fastboot/usb_transport_sniffer.h
rename to fastboot/fuzzy_fastboot/transport_sniffer.h
index 8119aea..2cbb9fe 100644
--- a/fastboot/fuzzy_fastboot/usb_transport_sniffer.h
+++ b/fastboot/fuzzy_fastboot/transport_sniffer.h
@@ -42,12 +42,12 @@
/* A special class for sniffing reads and writes
*
* A useful debugging tool is to see the raw fastboot transactions going between
- * the host and device. This class wraps the UsbTransport class, and snoops and saves
+ * the host and device. This class is a special subclass of Transport that snoops and saves
* all the transactions going on. Additionally, if there is a console serial port
* from the device, this class can monitor it as well and capture the interleaving of
* transport transactions and UART log messages.
*/
-class UsbTransportSniffer : public UsbTransport {
+class TransportSniffer : public Transport {
public:
enum EventType {
READ,
@@ -67,8 +67,8 @@
const std::vector<char> buf;
};
- UsbTransportSniffer(std::unique_ptr<UsbTransport> transport, const int serial_fd = 0);
- ~UsbTransportSniffer() override;
+ TransportSniffer(std::unique_ptr<Transport> transport, const int serial_fd = 0);
+ ~TransportSniffer() override;
virtual ssize_t Read(void* data, size_t len) override;
virtual ssize_t Write(const void* data, size_t len) override;
@@ -81,7 +81,7 @@
private:
std::vector<Event> transfers_;
- std::unique_ptr<UsbTransport> transport_;
+ std::unique_ptr<Transport> transport_;
const int serial_fd_;
};
diff --git a/fastboot/tcp.cpp b/fastboot/tcp.cpp
index dd6fbf8..dca306f 100644
--- a/fastboot/tcp.cpp
+++ b/fastboot/tcp.cpp
@@ -64,6 +64,7 @@
ssize_t Read(void* data, size_t length) override;
ssize_t Write(const void* data, size_t length) override;
int Close() override;
+ int Reset() override;
private:
explicit TcpTransport(std::unique_ptr<Socket> sock) : socket_(std::move(sock)) {}
@@ -178,6 +179,10 @@
return result;
}
+int TcpTransport::Reset() {
+ return 0;
+}
+
std::unique_ptr<Transport> Connect(const std::string& hostname, int port, std::string* error) {
return internal::Connect(Socket::NewClient(Socket::Protocol::kTcp, hostname, port, error),
error);
diff --git a/fastboot/transport.h b/fastboot/transport.h
index 96b90d2..de0cc92 100644
--- a/fastboot/transport.h
+++ b/fastboot/transport.h
@@ -36,6 +36,8 @@
// Closes the underlying transport. Returns 0 on success.
virtual int Close() = 0;
+ virtual int Reset() = 0;
+
// Blocks until the transport disconnects. Transports that don't support
// this will return immediately. Returns 0 on success.
virtual int WaitForDisconnect() { return 0; }
diff --git a/fastboot/udp.cpp b/fastboot/udp.cpp
index 53fb347..308c96c 100644
--- a/fastboot/udp.cpp
+++ b/fastboot/udp.cpp
@@ -109,6 +109,7 @@
ssize_t Read(void* data, size_t length) override;
ssize_t Write(const void* data, size_t length) override;
int Close() override;
+ int Reset() override;
private:
explicit UdpTransport(std::unique_ptr<Socket> socket) : socket_(std::move(socket)) {}
@@ -370,6 +371,10 @@
return result;
}
+int UdpTransport::Reset() {
+ return 0;
+}
+
std::unique_ptr<Transport> Connect(const std::string& hostname, int port, std::string* error) {
return internal::Connect(Socket::NewClient(Socket::Protocol::kUdp, hostname, port, error),
error);
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 3d4a62a..3463a68 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -954,6 +954,10 @@
return true;
}
+static bool SupportsCheckpoint(FstabEntry* entry) {
+ return entry->fs_mgr_flags.checkpoint_blk || entry->fs_mgr_flags.checkpoint_fs;
+}
+
class CheckpointManager {
public:
CheckpointManager(int needs_checkpoint = -1) : needs_checkpoint_(needs_checkpoint) {}
@@ -970,7 +974,7 @@
}
bool Update(FstabEntry* entry, const std::string& block_device = std::string()) {
- if (!entry->fs_mgr_flags.checkpoint_blk && !entry->fs_mgr_flags.checkpoint_fs) {
+ if (!SupportsCheckpoint(entry)) {
return true;
}
@@ -991,7 +995,7 @@
}
bool Revert(FstabEntry* entry) {
- if (!entry->fs_mgr_flags.checkpoint_blk && !entry->fs_mgr_flags.checkpoint_fs) {
+ if (!SupportsCheckpoint(entry)) {
return true;
}
@@ -1132,7 +1136,9 @@
return FS_MGR_MNTALL_FAIL;
}
- for (size_t i = 0; i < fstab->size(); i++) {
+ // Keep i int to prevent unsigned integer overflow from (i = top_idx - 1),
+ // where top_idx is 0. It will give SIGABRT
+ for (int i = 0; i < static_cast<int>(fstab->size()); i++) {
auto& current_entry = (*fstab)[i];
// If a filesystem should have been mounted in the first stage, we
@@ -1406,6 +1412,7 @@
}
static bool fs_mgr_unmount_all_data_mounts(const std::string& block_device) {
+ LINFO << __FUNCTION__ << "(): about to umount everything on top of " << block_device;
Timer t;
// TODO(b/135984674): should be configured via a read-only property.
std::chrono::milliseconds timeout = 5s;
@@ -1413,22 +1420,34 @@
bool umount_done = true;
Fstab proc_mounts;
if (!ReadFstabFromFile("/proc/mounts", &proc_mounts)) {
- LERROR << "Can't read /proc/mounts";
+ LERROR << __FUNCTION__ << "(): Can't read /proc/mounts";
return false;
}
// Now proceed with other bind mounts on top of /data.
for (const auto& entry : proc_mounts) {
if (entry.blk_device == block_device) {
if (umount2(entry.mount_point.c_str(), 0) != 0) {
+ PERROR << __FUNCTION__ << "(): Failed to umount " << entry.mount_point;
umount_done = false;
}
}
}
if (umount_done) {
- LINFO << "Unmounting /data took " << t;
+ LINFO << __FUNCTION__ << "(): Unmounting /data took " << t;
return true;
}
if (t.duration() > timeout) {
+ LERROR << __FUNCTION__ << "(): Timed out unmounting all mounts on " << block_device;
+ Fstab remaining_mounts;
+ if (!ReadFstabFromFile("/proc/mounts", &remaining_mounts)) {
+ LERROR << __FUNCTION__ << "(): Can't read /proc/mounts";
+ } else {
+ LERROR << __FUNCTION__ << "(): Following mounts remaining";
+ for (const auto& e : remaining_mounts) {
+ LERROR << __FUNCTION__ << "(): mount point: " << e.mount_point
+ << " block device: " << e.blk_device;
+ }
+ }
return false;
}
std::this_thread::sleep_for(50ms);
@@ -1454,18 +1473,20 @@
LERROR << "Can't find /data in fstab";
return -1;
}
- if (!fstab_entry->fs_mgr_flags.checkpoint_blk && !fstab_entry->fs_mgr_flags.checkpoint_fs) {
+ bool force_umount = GetBoolProperty("sys.init.userdata_remount.force_umount", false);
+ if (force_umount) {
+ LINFO << "Will force an umount of userdata even if it's not required";
+ }
+ if (!force_umount && !SupportsCheckpoint(fstab_entry)) {
LINFO << "Userdata doesn't support checkpointing. Nothing to do";
return 0;
}
CheckpointManager checkpoint_manager;
- if (!checkpoint_manager.NeedsCheckpoint()) {
+ if (!force_umount && !checkpoint_manager.NeedsCheckpoint()) {
LINFO << "Checkpointing not needed. Don't remount";
return 0;
}
- bool force_umount_for_f2fs =
- GetBoolProperty("sys.init.userdata_remount.force_umount_f2fs", false);
- if (fstab_entry->fs_mgr_flags.checkpoint_fs && !force_umount_for_f2fs) {
+ if (!force_umount && fstab_entry->fs_mgr_flags.checkpoint_fs) {
// Userdata is f2fs, simply remount it.
if (!checkpoint_manager.Update(fstab_entry)) {
LERROR << "Failed to remount userdata in checkpointing mode";
diff --git a/fs_mgr/libfiemap/Android.bp b/fs_mgr/libfiemap/Android.bp
index fdc1583..1bf457f 100644
--- a/fs_mgr/libfiemap/Android.bp
+++ b/fs_mgr/libfiemap/Android.bp
@@ -24,6 +24,7 @@
name: "libfiemap_srcs",
srcs: [
"fiemap_writer.cpp",
+ "fiemap_status.cpp",
"image_manager.cpp",
"metadata.cpp",
"split_fiemap_writer.cpp",
@@ -78,6 +79,11 @@
srcs: [
"fiemap_writer_test.cpp",
],
+
+ test_suites: ["vts-core", "device-tests"],
+ auto_gen_config: true,
+ test_min_api_level: 29,
+ require_root: true,
}
cc_test {
@@ -99,7 +105,3 @@
"image_test.cpp",
],
}
-
-vts_config {
- name: "VtsFiemapWriterTest",
-}
diff --git a/fs_mgr/libfiemap/AndroidTest.xml b/fs_mgr/libfiemap/AndroidTest.xml
deleted file mode 100644
index 44c80fc..0000000
--- a/fs_mgr/libfiemap/AndroidTest.xml
+++ /dev/null
@@ -1,30 +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 VTS VtsFiemapWriterTest">
- <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="VtsFiemapWriterTest"/>
- <option name="binary-test-source" value="_32bit::DATA/nativetest/fiemap_writer_test/fiemap_writer_test" />
- <option name="binary-test-source" value="_64bit::DATA/nativetest64/fiemap_writer_test/fiemap_writer_test" />
- <option name="binary-test-type" value="gtest"/>
- <option name="precondition-first-api-level" value="29" />
- <option name="test-timeout" value="1m"/>
- </test>
-</configuration>
diff --git a/fs_mgr/libfiemap/binder.cpp b/fs_mgr/libfiemap/binder.cpp
index f99055a..96c36ed 100644
--- a/fs_mgr/libfiemap/binder.cpp
+++ b/fs_mgr/libfiemap/binder.cpp
@@ -17,6 +17,7 @@
#if !defined(__ANDROID_RECOVERY__)
#include <android-base/logging.h>
#include <android-base/properties.h>
+#include <android/gsi/BnProgressCallback.h>
#include <android/gsi/IGsiService.h>
#include <android/gsi/IGsid.h>
#include <binder/IServiceManager.h>
@@ -29,10 +30,29 @@
using namespace android::gsi;
using namespace std::chrono_literals;
+class ProgressCallback final : public BnProgressCallback {
+ public:
+ ProgressCallback(std::function<bool(uint64_t, uint64_t)>&& callback)
+ : callback_(std::move(callback)) {
+ CHECK(callback_);
+ }
+ android::binder::Status onProgress(int64_t current, int64_t total) {
+ if (callback_(static_cast<uint64_t>(current), static_cast<uint64_t>(total))) {
+ return android::binder::Status::ok();
+ }
+ return android::binder::Status::fromServiceSpecificError(UNKNOWN_ERROR,
+ "Progress callback failed");
+ }
+
+ private:
+ std::function<bool(uint64_t, uint64_t)> callback_;
+};
+
class ImageManagerBinder final : public IImageManager {
public:
ImageManagerBinder(android::sp<IGsiService>&& service, android::sp<IImageService>&& manager);
- bool CreateBackingImage(const std::string& name, uint64_t size, int flags) override;
+ FiemapStatus CreateBackingImage(const std::string& name, uint64_t size, int flags,
+ std::function<bool(uint64_t, uint64_t)>&& on_progress) override;
bool DeleteBackingImage(const std::string& name) override;
bool MapImageDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms,
std::string* path) override;
@@ -41,7 +61,7 @@
bool IsImageMapped(const std::string& name) override;
bool MapImageWithDeviceMapper(const IPartitionOpener& opener, const std::string& name,
std::string* dev) override;
- bool ZeroFillNewImage(const std::string& name, uint64_t bytes) override;
+ FiemapStatus ZeroFillNewImage(const std::string& name, uint64_t bytes) override;
bool RemoveAllImages() override;
bool DisableImage(const std::string& name) override;
bool RemoveDisabledImages() override;
@@ -55,18 +75,31 @@
android::sp<IImageService> manager_;
};
+static FiemapStatus ToFiemapStatus(const char* func, const binder::Status& status) {
+ if (!status.isOk()) {
+ LOG(ERROR) << func << " binder returned: " << status.toString8().string();
+ if (status.serviceSpecificErrorCode() != 0) {
+ return FiemapStatus::FromErrorCode(status.serviceSpecificErrorCode());
+ } else {
+ return FiemapStatus::Error();
+ }
+ }
+ return FiemapStatus::Ok();
+}
+
ImageManagerBinder::ImageManagerBinder(android::sp<IGsiService>&& service,
android::sp<IImageService>&& manager)
: service_(std::move(service)), manager_(std::move(manager)) {}
-bool ImageManagerBinder::CreateBackingImage(const std::string& name, uint64_t size, int flags) {
- auto status = manager_->createBackingImage(name, size, flags);
- if (!status.isOk()) {
- LOG(ERROR) << __PRETTY_FUNCTION__
- << " binder returned: " << status.exceptionMessage().string();
- return false;
+FiemapStatus ImageManagerBinder::CreateBackingImage(
+ const std::string& name, uint64_t size, int flags,
+ std::function<bool(uint64_t, uint64_t)>&& on_progress) {
+ sp<IProgressCallback> callback = nullptr;
+ if (on_progress) {
+ callback = new ProgressCallback(std::move(on_progress));
}
- return true;
+ auto status = manager_->createBackingImage(name, size, flags, callback);
+ return ToFiemapStatus(__PRETTY_FUNCTION__, status);
}
bool ImageManagerBinder::DeleteBackingImage(const std::string& name) {
@@ -147,14 +180,9 @@
return retval;
}
-bool ImageManagerBinder::ZeroFillNewImage(const std::string& name, uint64_t bytes) {
+FiemapStatus ImageManagerBinder::ZeroFillNewImage(const std::string& name, uint64_t bytes) {
auto status = manager_->zeroFillNewImage(name, bytes);
- if (!status.isOk()) {
- LOG(ERROR) << __PRETTY_FUNCTION__
- << " binder returned: " << status.exceptionMessage().string();
- return false;
- }
- return true;
+ return ToFiemapStatus(__PRETTY_FUNCTION__, status);
}
bool ImageManagerBinder::RemoveAllImages() {
diff --git a/fs_mgr/libfiemap/fiemap_status.cpp b/fs_mgr/libfiemap/fiemap_status.cpp
new file mode 100644
index 0000000..92ac935
--- /dev/null
+++ b/fs_mgr/libfiemap/fiemap_status.cpp
@@ -0,0 +1,42 @@
+/*
+ * 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 <libfiemap/fiemap_status.h>
+
+namespace android::fiemap {
+
+// FiemapStatus -> string
+std::string FiemapStatus::string() const {
+ if (error_code() == ErrorCode::ERROR) {
+ return "Error";
+ }
+ return strerror(-static_cast<int>(error_code()));
+}
+
+// -errno -> known ErrorCode
+// unknown ErrorCode -> known ErrorCode
+FiemapStatus::ErrorCode FiemapStatus::CastErrorCode(int error_code) {
+ switch (error_code) {
+ case static_cast<int32_t>(ErrorCode::SUCCESS):
+ case static_cast<int32_t>(ErrorCode::NO_SPACE):
+ return static_cast<ErrorCode>(error_code);
+ case static_cast<int32_t>(ErrorCode::ERROR):
+ default:
+ return ErrorCode::ERROR;
+ }
+}
+
+} // namespace android::fiemap
diff --git a/fs_mgr/libfiemap/fiemap_writer.cpp b/fs_mgr/libfiemap/fiemap_writer.cpp
index d34e0b8..b911234 100644
--- a/fs_mgr/libfiemap/fiemap_writer.cpp
+++ b/fs_mgr/libfiemap/fiemap_writer.cpp
@@ -262,9 +262,9 @@
return true;
}
-static bool FallocateFallback(int file_fd, uint64_t block_size, uint64_t file_size,
- const std::string& file_path,
- const std::function<bool(uint64_t, uint64_t)>& on_progress) {
+static FiemapStatus FallocateFallback(int file_fd, uint64_t block_size, uint64_t file_size,
+ const std::string& file_path,
+ const std::function<bool(uint64_t, uint64_t)>& on_progress) {
// Even though this is much faster than writing zeroes, it is still slow
// enough that we need to fire the progress callback periodically. To
// easily achieve this, we seek in chunks. We use 1000 chunks since
@@ -280,22 +280,22 @@
auto rv = TEMP_FAILURE_RETRY(lseek(file_fd, cursor - 1, SEEK_SET));
if (rv < 0) {
PLOG(ERROR) << "Failed to lseek " << file_path;
- return false;
+ return FiemapStatus::FromErrno(errno);
}
if (rv != cursor - 1) {
LOG(ERROR) << "Seek returned wrong offset " << rv << " for file " << file_path;
- return false;
+ return FiemapStatus::Error();
}
char buffer[] = {0};
if (!android::base::WriteFully(file_fd, buffer, 1)) {
PLOG(ERROR) << "Write failed: " << file_path;
- return false;
+ return FiemapStatus::FromErrno(errno);
}
if (on_progress && !on_progress(cursor, file_size)) {
- return false;
+ return FiemapStatus::Error();
}
}
- return true;
+ return FiemapStatus::Ok();
}
// F2FS-specific ioctl
@@ -382,19 +382,19 @@
// write zeroes in 'blocksz' byte increments until we reach file_size to make sure the data
// blocks are actually written to by the file system and thus getting rid of the holes in the
// file.
-static bool WriteZeroes(int file_fd, const std::string& file_path, size_t blocksz,
- uint64_t file_size,
- const std::function<bool(uint64_t, uint64_t)>& on_progress) {
+static FiemapStatus WriteZeroes(int file_fd, const std::string& file_path, size_t blocksz,
+ uint64_t file_size,
+ const std::function<bool(uint64_t, uint64_t)>& on_progress) {
auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, blocksz), free);
if (buffer == nullptr) {
LOG(ERROR) << "failed to allocate memory for writing file";
- return false;
+ return FiemapStatus::Error();
}
off64_t offset = lseek64(file_fd, 0, SEEK_SET);
if (offset < 0) {
PLOG(ERROR) << "Failed to seek at the beginning of : " << file_path;
- return false;
+ return FiemapStatus::FromErrno(errno);
}
int permille = -1;
@@ -402,7 +402,7 @@
if (!::android::base::WriteFully(file_fd, buffer.get(), blocksz)) {
PLOG(ERROR) << "Failed to write" << blocksz << " bytes at offset" << offset
<< " in file " << file_path;
- return false;
+ return FiemapStatus::FromErrno(errno);
}
offset += blocksz;
@@ -412,7 +412,7 @@
int new_permille = (static_cast<uint64_t>(offset) * 1000) / file_size;
if (new_permille != permille && static_cast<uint64_t>(offset) != file_size) {
if (on_progress && !on_progress(offset, file_size)) {
- return false;
+ return FiemapStatus::Error();
}
permille = new_permille;
}
@@ -420,18 +420,18 @@
if (lseek64(file_fd, 0, SEEK_SET) < 0) {
PLOG(ERROR) << "Failed to reset offset at the beginning of : " << file_path;
- return false;
+ return FiemapStatus::FromErrno(errno);
}
- return true;
+ return FiemapStatus::Ok();
}
// Reserve space for the file on the file system and write it out to make sure the extents
// don't come back unwritten. Return from this function with the kernel file offset set to 0.
// If the filesystem is f2fs, then we also PIN the file on disk to make sure the blocks
// aren't moved around.
-static bool AllocateFile(int file_fd, const std::string& file_path, uint64_t blocksz,
- uint64_t file_size, unsigned int fs_type,
- std::function<bool(uint64_t, uint64_t)> on_progress) {
+static FiemapStatus AllocateFile(int file_fd, const std::string& file_path, uint64_t blocksz,
+ uint64_t file_size, unsigned int fs_type,
+ std::function<bool(uint64_t, uint64_t)> on_progress) {
bool need_explicit_writes = true;
switch (fs_type) {
case EXT4_SUPER_MAGIC:
@@ -439,11 +439,11 @@
case F2FS_SUPER_MAGIC: {
bool supported;
if (!F2fsPinBeforeAllocate(file_fd, &supported)) {
- return false;
+ return FiemapStatus::Error();
}
if (supported) {
if (!PinFile(file_fd, file_path, fs_type)) {
- return false;
+ return FiemapStatus::Error();
}
need_explicit_writes = false;
}
@@ -455,29 +455,32 @@
return FallocateFallback(file_fd, blocksz, file_size, file_path, on_progress);
default:
LOG(ERROR) << "Missing fallocate() support for file system " << fs_type;
- return false;
+ return FiemapStatus::Error();
}
if (fallocate(file_fd, 0, 0, file_size)) {
PLOG(ERROR) << "Failed to allocate space for file: " << file_path << " size: " << file_size;
- return false;
+ return FiemapStatus::FromErrno(errno);
}
- if (need_explicit_writes && !WriteZeroes(file_fd, file_path, blocksz, file_size, on_progress)) {
- return false;
+ if (need_explicit_writes) {
+ auto status = WriteZeroes(file_fd, file_path, blocksz, file_size, on_progress);
+ if (!status.is_ok()) {
+ return status;
+ }
}
// flush all writes here ..
if (fsync(file_fd)) {
PLOG(ERROR) << "Failed to synchronize written file:" << file_path;
- return false;
+ return FiemapStatus::FromErrno(errno);
}
// Send one last progress notification.
if (on_progress && !on_progress(file_size, file_size)) {
- return false;
+ return FiemapStatus::Error();
}
- return true;
+ return FiemapStatus::Ok();
}
bool FiemapWriter::HasPinnedExtents(const std::string& file_path) {
@@ -671,6 +674,18 @@
FiemapUniquePtr FiemapWriter::Open(const std::string& file_path, uint64_t file_size, bool create,
std::function<bool(uint64_t, uint64_t)> progress) {
+ FiemapUniquePtr ret;
+ if (!Open(file_path, file_size, &ret, create, progress).is_ok()) {
+ return nullptr;
+ }
+ return ret;
+}
+
+FiemapStatus FiemapWriter::Open(const std::string& file_path, uint64_t file_size,
+ FiemapUniquePtr* out, bool create,
+ std::function<bool(uint64_t, uint64_t)> progress) {
+ out->reset();
+
// if 'create' is false, open an existing file and do not truncate.
int open_flags = O_RDWR | O_CLOEXEC;
if (create) {
@@ -683,43 +698,46 @@
TEMP_FAILURE_RETRY(open(file_path.c_str(), open_flags, S_IRUSR | S_IWUSR)));
if (file_fd < 0) {
PLOG(ERROR) << "Failed to create file at: " << file_path;
- return nullptr;
+ return FiemapStatus::FromErrno(errno);
}
std::string abs_path;
if (!::android::base::Realpath(file_path, &abs_path)) {
+ int saved_errno = errno;
PLOG(ERROR) << "Invalid file path: " << file_path;
cleanup(file_path, create);
- return nullptr;
+ return FiemapStatus::FromErrno(saved_errno);
}
std::string bdev_path;
if (!GetBlockDeviceForFile(abs_path, &bdev_path)) {
LOG(ERROR) << "Failed to get block dev path for file: " << file_path;
cleanup(abs_path, create);
- return nullptr;
+ return FiemapStatus::Error();
}
::android::base::unique_fd bdev_fd(
TEMP_FAILURE_RETRY(open(bdev_path.c_str(), O_RDONLY | O_CLOEXEC)));
if (bdev_fd < 0) {
+ int saved_errno = errno;
PLOG(ERROR) << "Failed to open block device: " << bdev_path;
cleanup(file_path, create);
- return nullptr;
+ return FiemapStatus::FromErrno(saved_errno);
}
uint64_t bdevsz;
if (!GetBlockDeviceSize(bdev_fd, bdev_path, &bdevsz)) {
+ int saved_errno = errno;
LOG(ERROR) << "Failed to get block device size for : " << bdev_path;
cleanup(file_path, create);
- return nullptr;
+ return FiemapStatus::FromErrno(saved_errno);
}
if (!create) {
file_size = GetFileSize(abs_path);
if (file_size == 0) {
LOG(ERROR) << "Invalid file size of zero bytes for file: " << abs_path;
- return nullptr;
+ return FiemapStatus::FromErrno(errno);
}
}
@@ -728,7 +746,7 @@
if (!PerformFileChecks(abs_path, &blocksz, &fs_type)) {
LOG(ERROR) << "Failed to validate file or file system for file:" << abs_path;
cleanup(abs_path, create);
- return nullptr;
+ return FiemapStatus::Error();
}
// Align up to the nearest block size.
@@ -737,11 +755,13 @@
}
if (create) {
- if (!AllocateFile(file_fd, abs_path, blocksz, file_size, fs_type, std::move(progress))) {
+ auto status =
+ AllocateFile(file_fd, abs_path, blocksz, file_size, fs_type, std::move(progress));
+ if (!status.is_ok()) {
LOG(ERROR) << "Failed to allocate file: " << abs_path << " of size: " << file_size
<< " bytes";
cleanup(abs_path, create);
- return nullptr;
+ return status;
}
}
@@ -749,7 +769,7 @@
if (!PinFile(file_fd, abs_path, fs_type)) {
cleanup(abs_path, create);
LOG(ERROR) << "Failed to pin the file in storage";
- return nullptr;
+ return FiemapStatus::Error();
}
// now allocate the FiemapWriter and start setting it up
@@ -760,14 +780,14 @@
if (!ReadFiemap(file_fd, abs_path, &fmap->extents_)) {
LOG(ERROR) << "Failed to read fiemap of file: " << abs_path;
cleanup(abs_path, create);
- return nullptr;
+ return FiemapStatus::Error();
}
break;
case MSDOS_SUPER_MAGIC:
if (!ReadFibmap(file_fd, abs_path, &fmap->extents_)) {
LOG(ERROR) << "Failed to read fibmap of file: " << abs_path;
cleanup(abs_path, create);
- return nullptr;
+ return FiemapStatus::Error();
}
break;
}
@@ -781,7 +801,8 @@
LOG(VERBOSE) << "Successfully created FiemapWriter for file " << abs_path << " on block device "
<< bdev_path;
- return fmap;
+ *out = std::move(fmap);
+ return FiemapStatus::Ok();
}
} // namespace fiemap
diff --git a/fs_mgr/libfiemap/fiemap_writer_test.cpp b/fs_mgr/libfiemap/fiemap_writer_test.cpp
index 4ac7161..22a3722 100644
--- a/fs_mgr/libfiemap/fiemap_writer_test.cpp
+++ b/fs_mgr/libfiemap/fiemap_writer_test.cpp
@@ -193,7 +193,9 @@
}
TEST_F(FiemapWriterTest, MaxBlockSize) {
- ASSERT_GT(DetermineMaximumFileSize(testfile), 0);
+ uint64_t max_piece_size = 0;
+ ASSERT_TRUE(DetermineMaximumFileSize(testfile, &max_piece_size));
+ ASSERT_GT(max_piece_size, 0);
}
TEST_F(FiemapWriterTest, FibmapBlockAddressing) {
diff --git a/fs_mgr/libfiemap/image_manager.cpp b/fs_mgr/libfiemap/image_manager.cpp
index baa5de4..280318e 100644
--- a/fs_mgr/libfiemap/image_manager.cpp
+++ b/fs_mgr/libfiemap/image_manager.cpp
@@ -26,6 +26,7 @@
#include <fs_mgr_dm_linear.h>
#include <libdm/loop_control.h>
#include <libfiemap/split_fiemap_writer.h>
+#include <libgsi/libgsi.h>
#include "metadata.h"
#include "utility.h"
@@ -34,6 +35,7 @@
namespace fiemap {
using namespace std::literals;
+using android::base::ReadFileToString;
using android::base::unique_fd;
using android::dm::DeviceMapper;
using android::dm::DmDeviceState;
@@ -53,6 +55,11 @@
std::unique_ptr<ImageManager> ImageManager::Open(const std::string& dir_prefix) {
auto metadata_dir = "/metadata/gsi/" + dir_prefix;
auto data_dir = "/data/gsi/" + dir_prefix;
+ auto install_dir_file = gsi::DsuInstallDirFile(gsi::GetDsuSlot(dir_prefix));
+ std::string path;
+ if (ReadFileToString(install_dir_file, &path)) {
+ data_dir = path;
+ }
return Open(metadata_dir, data_dir);
}
@@ -133,27 +140,25 @@
return access(header_file.c_str(), F_OK) == 0;
}
-bool ImageManager::CreateBackingImage(const std::string& name, uint64_t size, int flags) {
- return CreateBackingImage(name, size, flags, nullptr);
-}
-
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/");
}
-bool ImageManager::CreateBackingImage(const std::string& name, uint64_t size, int flags,
- std::function<bool(uint64_t, uint64_t)>&& on_progress) {
+FiemapStatus ImageManager::CreateBackingImage(
+ const std::string& name, uint64_t size, int flags,
+ std::function<bool(uint64_t, uint64_t)>&& on_progress) {
auto data_path = GetImageHeaderPath(name);
- auto fw = SplitFiemap::Create(data_path, size, 0, on_progress);
- if (!fw) {
- return false;
+ std::unique_ptr<SplitFiemap> fw;
+ auto status = SplitFiemap::Create(data_path, size, 0, &fw, on_progress);
+ if (!status.is_ok()) {
+ return status;
}
bool reliable_pinning;
if (!FilesystemHasReliablePinning(data_path, &reliable_pinning)) {
- return false;
+ return FiemapStatus::Error();
}
if (!reliable_pinning && !IsUnreliablePinningAllowed(data_path)) {
// For historical reasons, we allow unreliable pinning for certain use
@@ -164,7 +169,7 @@
// proper pinning.
LOG(ERROR) << "File system does not have reliable block pinning";
SplitFiemap::RemoveSplitFiles(data_path);
- return false;
+ return FiemapStatus::Error();
}
// Except for testing, we do not allow persisting metadata that references
@@ -180,24 +185,25 @@
fw = {};
SplitFiemap::RemoveSplitFiles(data_path);
- return false;
+ return FiemapStatus::Error();
}
bool readonly = !!(flags & CREATE_IMAGE_READONLY);
if (!UpdateMetadata(metadata_dir_, name, fw.get(), size, readonly)) {
- return false;
+ return FiemapStatus::Error();
}
if (flags & CREATE_IMAGE_ZERO_FILL) {
- if (!ZeroFillNewImage(name, 0)) {
+ auto res = ZeroFillNewImage(name, 0);
+ if (!res.is_ok()) {
DeleteBackingImage(name);
- return false;
+ return res;
}
}
- return true;
+ return FiemapStatus::Ok();
}
-bool ImageManager::ZeroFillNewImage(const std::string& name, uint64_t bytes) {
+FiemapStatus ImageManager::ZeroFillNewImage(const std::string& name, uint64_t bytes) {
auto data_path = GetImageHeaderPath(name);
// See the comment in MapImageDevice() about how this works.
@@ -205,13 +211,13 @@
bool can_use_devicemapper;
if (!FiemapWriter::GetBlockDeviceForFile(data_path, &block_device, &can_use_devicemapper)) {
LOG(ERROR) << "Could not determine block device for " << data_path;
- return false;
+ return FiemapStatus::Error();
}
if (!can_use_devicemapper) {
// We've backed with loop devices, and since we store files in an
// unencrypted folder, the initial zeroes we wrote will suffice.
- return true;
+ return FiemapStatus::Ok();
}
// data is dm-crypt, or FBE + dm-default-key. This means the zeroes written
@@ -219,7 +225,7 @@
// this.
auto device = MappedDevice::Open(this, 10s, name);
if (!device) {
- return false;
+ return FiemapStatus::Error();
}
static constexpr size_t kChunkSize = 4096;
@@ -232,7 +238,7 @@
remaining = get_block_device_size(device->fd());
if (!remaining) {
PLOG(ERROR) << "Could not get block device size for " << device->path();
- return false;
+ return FiemapStatus::FromErrno(errno);
}
}
while (remaining) {
@@ -240,11 +246,11 @@
if (!android::base::WriteFully(device->fd(), zeroes.data(),
static_cast<size_t>(to_write))) {
PLOG(ERROR) << "write failed: " << device->path();
- return false;
+ return FiemapStatus::FromErrno(errno);
}
remaining -= to_write;
}
- return true;
+ return FiemapStatus::Ok();
}
bool ImageManager::DeleteBackingImage(const std::string& name) {
diff --git a/fs_mgr/libfiemap/include/libfiemap/fiemap_status.h b/fs_mgr/libfiemap/include/libfiemap/fiemap_status.h
new file mode 100644
index 0000000..d7b2cf1
--- /dev/null
+++ b/fs_mgr/libfiemap/include/libfiemap/fiemap_status.h
@@ -0,0 +1,68 @@
+/*
+ * 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 <errno.h>
+#include <stdint.h>
+
+#include <string>
+
+namespace android::fiemap {
+
+// Represent error status of libfiemap classes.
+class FiemapStatus {
+ public:
+ enum class ErrorCode : int32_t {
+ SUCCESS = 0,
+ // Generic non-recoverable failure.
+ ERROR = INT32_MIN,
+ // Not enough space
+ NO_SPACE = -ENOSPC,
+ };
+
+ // Create from a given errno (specified in errno,h)
+ static FiemapStatus FromErrno(int error_num) { return FiemapStatus(CastErrorCode(-error_num)); }
+
+ // Create from an integer error code that is expected to be an ErrorCode
+ // value. If it isn't, Error() is returned.
+ static FiemapStatus FromErrorCode(int32_t error_code) {
+ return FiemapStatus(CastErrorCode(error_code));
+ }
+
+ // Generic error.
+ static FiemapStatus Error() { return FiemapStatus(ErrorCode::ERROR); }
+
+ // Success.
+ static FiemapStatus Ok() { return FiemapStatus(ErrorCode::SUCCESS); }
+
+ ErrorCode error_code() const { return error_code_; }
+ bool is_ok() const { return error_code() == ErrorCode::SUCCESS; }
+ operator bool() const { return is_ok(); }
+
+ // For logging and debugging only.
+ std::string string() const;
+
+ protected:
+ FiemapStatus(ErrorCode code) : error_code_(code) {}
+
+ private:
+ ErrorCode error_code_;
+
+ static ErrorCode CastErrorCode(int error);
+};
+
+} // namespace android::fiemap
diff --git a/fs_mgr/libfiemap/include/libfiemap/fiemap_writer.h b/fs_mgr/libfiemap/include/libfiemap/fiemap_writer.h
index c692265..dd345f6 100644
--- a/fs_mgr/libfiemap/include/libfiemap/fiemap_writer.h
+++ b/fs_mgr/libfiemap/include/libfiemap/fiemap_writer.h
@@ -27,6 +27,8 @@
#include <android-base/unique_fd.h>
+#include <libfiemap/fiemap_status.h>
+
namespace android {
namespace fiemap {
@@ -47,6 +49,9 @@
static FiemapUniquePtr Open(const std::string& file_path, uint64_t file_size,
bool create = true,
std::function<bool(uint64_t, uint64_t)> progress = {});
+ static FiemapStatus Open(const std::string& file_path, uint64_t file_size, FiemapUniquePtr* out,
+ bool create = true,
+ std::function<bool(uint64_t, uint64_t)> progress = {});
// Check that a file still has the same extents since it was last opened with FiemapWriter,
// assuming the file was not resized outside of FiemapWriter. Returns false either on error
diff --git a/fs_mgr/libfiemap/include/libfiemap/image_manager.h b/fs_mgr/libfiemap/include/libfiemap/image_manager.h
index 7b907c0..2c13229 100644
--- a/fs_mgr/libfiemap/include/libfiemap/image_manager.h
+++ b/fs_mgr/libfiemap/include/libfiemap/image_manager.h
@@ -25,6 +25,7 @@
#include <string>
#include <android-base/unique_fd.h>
+#include <libfiemap/fiemap_status.h>
#include <liblp/partition_opener.h>
namespace android {
@@ -52,7 +53,9 @@
// of the image is undefined. If zero-fill is requested, and the operation
// cannot be completed, the image will be deleted and this function will
// return false.
- virtual bool CreateBackingImage(const std::string& name, uint64_t size, int flags) = 0;
+ virtual FiemapStatus CreateBackingImage(
+ const std::string& name, uint64_t size, int flags,
+ std::function<bool(uint64_t, uint64_t)>&& on_progress = nullptr) = 0;
// Delete an image created with CreateBackingImage. Its entry will be
// removed from the associated lp_metadata file.
@@ -113,7 +116,7 @@
// Writes |bytes| zeros to |name| file. If |bytes| is 0, then the
// whole file if filled with zeros.
- virtual bool ZeroFillNewImage(const std::string& name, uint64_t bytes) = 0;
+ virtual FiemapStatus ZeroFillNewImage(const std::string& name, uint64_t bytes) = 0;
// Find and remove all images and metadata for this manager.
virtual bool RemoveAllImages() = 0;
@@ -133,7 +136,8 @@
static std::unique_ptr<ImageManager> Open(const std::string& dir_prefix);
// Methods that must be implemented from IImageManager.
- bool CreateBackingImage(const std::string& name, uint64_t size, int flags) override;
+ FiemapStatus CreateBackingImage(const std::string& name, uint64_t size, int flags,
+ std::function<bool(uint64_t, uint64_t)>&& on_progress) override;
bool DeleteBackingImage(const std::string& name) override;
bool MapImageDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms,
std::string* path) override;
@@ -149,9 +153,6 @@
bool MapAllImages(const std::function<bool(std::set<std::string>)>& init) override;
std::vector<std::string> GetAllBackingImages();
- // Same as CreateBackingImage, but provides a progress notification.
- bool CreateBackingImage(const std::string& name, uint64_t size, int flags,
- std::function<bool(uint64_t, uint64_t)>&& on_progress);
// Returns true if the named partition exists. This does not check the
// consistency of the backing image/data file.
@@ -164,7 +165,7 @@
void set_partition_opener(std::unique_ptr<IPartitionOpener>&& opener);
// Writes |bytes| zeros at the beginning of the passed image
- bool ZeroFillNewImage(const std::string& name, uint64_t bytes);
+ FiemapStatus ZeroFillNewImage(const std::string& name, uint64_t bytes);
private:
ImageManager(const std::string& metadata_dir, const std::string& data_dir);
diff --git a/fs_mgr/libfiemap/include/libfiemap/split_fiemap_writer.h b/fs_mgr/libfiemap/include/libfiemap/split_fiemap_writer.h
index feffb3d..d739fcf 100644
--- a/fs_mgr/libfiemap/include/libfiemap/split_fiemap_writer.h
+++ b/fs_mgr/libfiemap/include/libfiemap/split_fiemap_writer.h
@@ -25,7 +25,8 @@
#include <android-base/unique_fd.h>
-#include "fiemap_writer.h"
+#include <libfiemap/fiemap_status.h>
+#include <libfiemap/fiemap_writer.h>
namespace android {
namespace fiemap {
@@ -43,6 +44,9 @@
static std::unique_ptr<SplitFiemap> Create(const std::string& file_path, uint64_t file_size,
uint64_t max_piece_size,
ProgressCallback progress = {});
+ static FiemapStatus Create(const std::string& file_path, uint64_t file_size,
+ uint64_t max_piece_size, std::unique_ptr<SplitFiemap>* out_val,
+ ProgressCallback progress = {});
// Open an existing split fiemap file.
static std::unique_ptr<SplitFiemap> Open(const std::string& file_path);
diff --git a/fs_mgr/libfiemap/split_fiemap_writer.cpp b/fs_mgr/libfiemap/split_fiemap_writer.cpp
index cc54f20..12c7397 100644
--- a/fs_mgr/libfiemap/split_fiemap_writer.cpp
+++ b/fs_mgr/libfiemap/split_fiemap_writer.cpp
@@ -45,16 +45,28 @@
std::unique_ptr<SplitFiemap> SplitFiemap::Create(const std::string& file_path, uint64_t file_size,
uint64_t max_piece_size,
ProgressCallback progress) {
+ std::unique_ptr<SplitFiemap> ret;
+ if (!Create(file_path, file_size, max_piece_size, &ret, progress).is_ok()) {
+ return nullptr;
+ }
+ return ret;
+}
+
+FiemapStatus SplitFiemap::Create(const std::string& file_path, uint64_t file_size,
+ uint64_t max_piece_size, std::unique_ptr<SplitFiemap>* out_val,
+ ProgressCallback progress) {
+ out_val->reset();
+
if (!file_size) {
LOG(ERROR) << "Cannot create a fiemap for a 0-length file: " << file_path;
- return nullptr;
+ return FiemapStatus::Error();
}
if (!max_piece_size) {
- max_piece_size = DetermineMaximumFileSize(file_path);
- if (!max_piece_size) {
+ auto status = DetermineMaximumFileSize(file_path, &max_piece_size);
+ if (!status.is_ok()) {
LOG(ERROR) << "Could not determine maximum file size for " << file_path;
- return nullptr;
+ return status;
}
}
@@ -75,7 +87,6 @@
}
return true;
};
-
std::unique_ptr<SplitFiemap> out(new SplitFiemap());
out->creating_ = true;
out->list_file_ = file_path;
@@ -85,14 +96,17 @@
while (remaining_bytes) {
if (out->files_.size() >= kMaxFilePieces) {
LOG(ERROR) << "Requested size " << file_size << " created too many split files";
- return nullptr;
+ out.reset();
+ return FiemapStatus::Error();
}
std::string chunk_path =
android::base::StringPrintf("%s.%04d", file_path.c_str(), (int)out->files_.size());
uint64_t chunk_size = std::min(max_piece_size, remaining_bytes);
- auto writer = FiemapWriter::Open(chunk_path, chunk_size, true, on_progress);
- if (!writer) {
- return nullptr;
+ FiemapUniquePtr writer;
+ auto status = FiemapWriter::Open(chunk_path, chunk_size, &writer, true, on_progress);
+ if (!status.is_ok()) {
+ out.reset();
+ return status;
}
// To make sure the alignment doesn't create too much inconsistency, we
@@ -110,20 +124,23 @@
unique_fd fd(open(out->list_file_.c_str(), O_CREAT | O_WRONLY | O_CLOEXEC, 0660));
if (fd < 0) {
PLOG(ERROR) << "Failed to open " << file_path;
- return nullptr;
+ out.reset();
+ return FiemapStatus::FromErrno(errno);
}
for (const auto& writer : out->files_) {
std::string line = android::base::Basename(writer->file_path()) + "\n";
if (!android::base::WriteFully(fd, line.data(), line.size())) {
PLOG(ERROR) << "Write failed " << file_path;
- return nullptr;
+ out.reset();
+ return FiemapStatus::FromErrno(errno);
}
}
// Unset this bit, so we don't unlink on destruction.
out->creating_ = false;
- return out;
+ *out_val = std::move(out);
+ return FiemapStatus::Ok();
}
std::unique_ptr<SplitFiemap> SplitFiemap::Open(const std::string& file_path) {
diff --git a/fs_mgr/libfiemap/utility.cpp b/fs_mgr/libfiemap/utility.cpp
index 955e544..bbb0510 100644
--- a/fs_mgr/libfiemap/utility.cpp
+++ b/fs_mgr/libfiemap/utility.cpp
@@ -37,29 +37,30 @@
static constexpr char kUserdataDevice[] = "/dev/block/by-name/userdata";
-uint64_t DetermineMaximumFileSize(const std::string& file_path) {
+FiemapStatus DetermineMaximumFileSize(const std::string& file_path, uint64_t* result) {
// Create the smallest file possible (one block).
- auto writer = FiemapWriter::Open(file_path, 1);
- if (!writer) {
- return 0;
+ FiemapUniquePtr writer;
+ auto status = FiemapWriter::Open(file_path, 1, &writer);
+ if (!status.is_ok()) {
+ return status;
}
- uint64_t result = 0;
+ *result = 0;
switch (writer->fs_type()) {
case EXT4_SUPER_MAGIC:
// The minimum is 16GiB, so just report that. If we wanted we could parse the
// superblock and figure out if 64-bit support is enabled.
- result = 17179869184ULL;
+ *result = 17179869184ULL;
break;
case F2FS_SUPER_MAGIC:
// Formula is from https://www.kernel.org/doc/Documentation/filesystems/f2fs.txt
// 4KB * (923 + 2 * 1018 + 2 * 1018 * 1018 + 1018 * 1018 * 1018) := 3.94TB.
- result = 4329690886144ULL;
+ *result = 4329690886144ULL;
break;
case MSDOS_SUPER_MAGIC:
// 4GB-1, which we want aligned to the block size.
- result = 4294967295;
- result -= (result % writer->block_size());
+ *result = 4294967295;
+ *result -= (*result % writer->block_size());
break;
default:
LOG(ERROR) << "Unknown file system type: " << writer->fs_type();
@@ -70,7 +71,7 @@
writer = nullptr;
unlink(file_path.c_str());
- return result;
+ return FiemapStatus::Ok();
}
// Given a SplitFiemap, this returns a device path that will work during first-
diff --git a/fs_mgr/libfiemap/utility.h b/fs_mgr/libfiemap/utility.h
index 24ebc57..4c0bc2b 100644
--- a/fs_mgr/libfiemap/utility.h
+++ b/fs_mgr/libfiemap/utility.h
@@ -28,7 +28,7 @@
// Given a file that will be created, determine the maximum size its containing
// filesystem allows. Note this is a theoretical maximum size; free space is
// ignored entirely.
-uint64_t DetermineMaximumFileSize(const std::string& file_path);
+FiemapStatus DetermineMaximumFileSize(const std::string& file_path, uint64_t* result);
// Given a SplitFiemap, this returns a device path that will work during first-
// stage init (i.e., its path can be found by InitRequiredDevices).
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index eadcecc..c67e33d 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -75,6 +75,7 @@
"snapshot.cpp",
"snapshot_metadata_updater.cpp",
"partition_cow_creator.cpp",
+ "return.cpp",
"utility.cpp",
],
}
@@ -127,6 +128,7 @@
"include_test",
],
srcs: [
+ "android/snapshot/snapshot.proto",
"test_helpers.cpp",
],
shared_libs: [
diff --git a/fs_mgr/libsnapshot/OWNERS b/fs_mgr/libsnapshot/OWNERS
index 0cfa7e4..801c446 100644
--- a/fs_mgr/libsnapshot/OWNERS
+++ b/fs_mgr/libsnapshot/OWNERS
@@ -1,2 +1,3 @@
+balsini@google.com
dvander@google.com
elsk@google.com
diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
index 629c3a4..a3a518d 100644
--- a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
+++ b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
@@ -85,3 +85,49 @@
// This is non-zero when |state| == MERGING or MERGE_COMPLETED.
uint64 metadata_sectors = 8;
}
+
+// Next: 8
+enum UpdateState {
+ // No update or merge is in progress.
+ None = 0;
+
+ // An update is applying; snapshots may already exist.
+ Initiated = 1;
+
+ // An update is pending, but has not been successfully booted yet.
+ Unverified = 2;
+
+ // The kernel is merging in the background.
+ Merging = 3;
+
+ // Post-merge cleanup steps could not be completed due to a transient
+ // error, but the next reboot will finish any pending operations.
+ MergeNeedsReboot = 4;
+
+ // Merging is complete, and needs to be acknowledged.
+ MergeCompleted = 5;
+
+ // Merging failed due to an unrecoverable error.
+ MergeFailed = 6;
+
+ // The update was implicitly cancelled, either by a rollback or a flash
+ // operation via fastboot. This state can only be returned by WaitForMerge.
+ Cancelled = 7;
+};
+
+// Next: 5
+message SnapshotUpdateStatus {
+ UpdateState state = 1;
+
+ // Total number of sectors allocated in the COW files before performing the
+ // merge operation. This field is used to keep track of the total number
+ // of sectors modified to monitor and show the progress of the merge during
+ // an update.
+ uint64 sectors_allocated = 2;
+
+ // Total number of sectors of all the snapshot devices.
+ uint64 total_sectors = 3;
+
+ // Sectors allocated for metadata in all the snapshot devices.
+ uint64 metadata_sectors = 4;
+}
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/return.h b/fs_mgr/libsnapshot/include/libsnapshot/return.h
new file mode 100644
index 0000000..1f132fa
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/return.h
@@ -0,0 +1,61 @@
+// 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 <stdint.h>
+#include <string.h>
+
+#include <libfiemap/fiemap_status.h>
+
+namespace android::snapshot {
+
+// SnapshotManager functions return either bool or Return objects. "Return" types provides
+// more information about the reason of the failure.
+class Return {
+ using FiemapStatus = android::fiemap::FiemapStatus;
+
+ public:
+ 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_; }
+ bool is_ok() const { return error_code() == ErrorCode::SUCCESS; }
+ operator bool() const { return is_ok(); }
+ // Total required size on /userdata.
+ uint64_t required_size() const { return required_size_; }
+ std::string string() const;
+
+ 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) {}
+
+ private:
+ ErrorCode error_code_;
+ uint64_t required_size_;
+ Return(ErrorCode error_code, uint64_t required_size = 0)
+ : error_code_(error_code), required_size_(required_size) {}
+
+ // FiemapStatus::ErrorCode -> ErrorCode
+ static ErrorCode FromFiemapStatusErrorCode(FiemapStatus::ErrorCode error_code);
+};
+
+} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 52f8794..959d8a7 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -26,6 +26,7 @@
#include <vector>
#include <android-base/unique_fd.h>
+#include <android/snapshot/snapshot.pb.h>
#include <fs_mgr_dm_linear.h>
#include <libdm/dm.h>
#include <libfiemap/image_manager.h>
@@ -34,6 +35,7 @@
#include <update_engine/update_metadata.pb.h>
#include <libsnapshot/auto_device.h>
+#include <libsnapshot/return.h>
#ifndef FRIEND_TEST
#define FRIEND_TEST(test_set_name, individual_test) \
@@ -80,35 +82,6 @@
NOT_CREATED,
};
-enum class UpdateState : unsigned int {
- // No update or merge is in progress.
- None,
-
- // An update is applying; snapshots may already exist.
- Initiated,
-
- // An update is pending, but has not been successfully booted yet.
- Unverified,
-
- // The kernel is merging in the background.
- Merging,
-
- // Post-merge cleanup steps could not be completed due to a transient
- // error, but the next reboot will finish any pending operations.
- MergeNeedsReboot,
-
- // Merging is complete, and needs to be acknowledged.
- MergeCompleted,
-
- // Merging failed due to an unrecoverable error.
- MergeFailed,
-
- // The update was implicitly cancelled, either by a rollback or a flash
- // operation via fastboot. This state can only be returned by WaitForMerge.
- Cancelled
-};
-std::ostream& operator<<(std::ostream& os, UpdateState state);
-
class SnapshotManager final {
using CreateLogicalPartitionParams = android::fs_mgr::CreateLogicalPartitionParams;
using IPartitionOpener = android::fs_mgr::IPartitionOpener;
@@ -116,6 +89,7 @@
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;
public:
// Dependency injection for testing.
@@ -207,9 +181,11 @@
// 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:
- // - true there is no merge or merge finishes
- // - false indicating an error has occurred
- bool WaitForMerge();
+ // - 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();
// Find the status of the current update, if any.
//
@@ -222,7 +198,7 @@
// 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.
- bool CreateUpdateSnapshots(const DeltaArchiveManifest& manifest);
+ Return CreateUpdateSnapshots(const DeltaArchiveManifest& manifest);
// Map a snapshotted partition for OTA clients to write to. Write-protected regions are
// determined previously in CreateSnapshots.
@@ -359,7 +335,7 @@
// |name| should be the base partition name (e.g. "system_a"). Create the
// backing COW image using the size previously passed to CreateSnapshot().
- bool CreateCowImage(LockedFile* lock, const std::string& name);
+ Return CreateCowImage(LockedFile* lock, const std::string& name);
// Map a snapshot device that was previously created with CreateSnapshot.
// If a merge was previously initiated, the device-mapper table will have a
@@ -411,7 +387,9 @@
// Interact with /metadata/ota/state.
UpdateState ReadUpdateState(LockedFile* file);
+ SnapshotUpdateStatus ReadSnapshotUpdateStatus(LockedFile* file);
bool WriteUpdateState(LockedFile* file, UpdateState state);
+ bool WriteSnapshotUpdateStatus(LockedFile* file, const SnapshotUpdateStatus& status);
std::string GetStateFilePath() const;
// Helpers for merging.
@@ -499,14 +477,14 @@
// Helper for CreateUpdateSnapshots.
// Creates all underlying images, COW partitions and snapshot files. Does not initialize them.
- bool CreateUpdateSnapshotsInternal(LockedFile* lock, const DeltaArchiveManifest& manifest,
- PartitionCowCreator* cow_creator,
- AutoDeviceList* created_devices,
- std::map<std::string, SnapshotStatus>* all_snapshot_status);
+ Return CreateUpdateSnapshotsInternal(
+ LockedFile* lock, const DeltaArchiveManifest& manifest,
+ PartitionCowCreator* cow_creator, AutoDeviceList* created_devices,
+ std::map<std::string, SnapshotStatus>* all_snapshot_status);
// Initialize snapshots so that they can be mapped later.
// Map the COW partition and zero-initialize the header.
- bool InitializeUpdateSnapshots(
+ Return InitializeUpdateSnapshots(
LockedFile* lock, MetadataBuilder* target_metadata,
const LpMetadata* exported_target_metadata, const std::string& target_suffix,
const std::map<std::string, SnapshotStatus>& all_snapshot_status);
diff --git a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
index 2bf1b57..98bf56a 100644
--- a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
@@ -14,10 +14,12 @@
#pragma once
+#include <memory>
#include <optional>
#include <string>
#include <unordered_set>
+#include <android-base/file.h>
#include <android/hardware/boot/1.1/IBootControl.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -40,7 +42,6 @@
using testing::_;
using testing::AssertionResult;
using testing::NiceMock;
-using testing::Return;
using namespace android::storage_literals;
using namespace std::string_literals;
@@ -115,6 +116,7 @@
class SnapshotTestPropertyFetcher : public android::fs_mgr::testing::MockPropertyFetcher {
public:
SnapshotTestPropertyFetcher(const std::string& slot_suffix) {
+ using testing::Return;
ON_CALL(*this, GetProperty("ro.boot.slot_suffix", _)).WillByDefault(Return(slot_suffix));
ON_CALL(*this, GetBoolProperty("ro.boot.dynamic_partitions", _))
.WillByDefault(Return(true));
@@ -155,5 +157,28 @@
// Get partition size from update package metadata.
uint64_t GetSize(PartitionUpdate* partition_update);
+// Util class for test cases on low space scenario. These tests assumes image manager
+// uses /data as backup device.
+class LowSpaceUserdata {
+ public:
+ // Set the maximum free space allowed for this test. If /userdata has more space than the given
+ // number, a file is allocated to consume space.
+ AssertionResult Init(uint64_t max_free_space);
+
+ uint64_t free_space() const;
+ uint64_t available_space() const;
+ uint64_t bsize() const;
+
+ private:
+ AssertionResult ReadUserdataStats();
+
+ static constexpr const char* kUserDataDevice = "/data";
+ std::unique_ptr<TemporaryFile> big_file_;
+ bool initialized_ = false;
+ uint64_t free_space_ = 0;
+ uint64_t available_space_ = 0;
+ uint64_t bsize_ = 0;
+};
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/return.cpp b/fs_mgr/libsnapshot/return.cpp
new file mode 100644
index 0000000..6559c12
--- /dev/null
+++ b/fs_mgr/libsnapshot/return.cpp
@@ -0,0 +1,46 @@
+// 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 <libsnapshot/return.h>
+
+#include <string.h>
+
+using android::fiemap::FiemapStatus;
+
+namespace android::snapshot {
+
+std::string Return::string() const {
+ switch (error_code()) {
+ case ErrorCode::ERROR:
+ return "Error";
+ case ErrorCode::NEEDS_REBOOT:
+ return "Retry after reboot";
+ case ErrorCode::SUCCESS:
+ [[fallthrough]];
+ case ErrorCode::NO_SPACE:
+ return strerror(-static_cast<int>(error_code()));
+ }
+}
+
+Return::ErrorCode Return::FromFiemapStatusErrorCode(FiemapStatus::ErrorCode error_code) {
+ switch (error_code) {
+ case FiemapStatus::ErrorCode::SUCCESS:
+ case FiemapStatus::ErrorCode::ERROR:
+ case FiemapStatus::ErrorCode::NO_SPACE:
+ return static_cast<ErrorCode>(error_code);
+ default:
+ return ErrorCode::ERROR;
+ }
+}
+} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index fd89ca0..88d6b8d 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -54,6 +54,7 @@
using android::dm::DmTargetSnapshot;
using android::dm::kSectorSize;
using android::dm::SnapshotStorageMode;
+using android::fiemap::FiemapStatus;
using android::fiemap::IImageManager;
using android::fs_mgr::CreateDmTable;
using android::fs_mgr::CreateLogicalPartition;
@@ -289,14 +290,14 @@
return true;
}
-bool SnapshotManager::CreateCowImage(LockedFile* lock, const std::string& name) {
+Return SnapshotManager::CreateCowImage(LockedFile* lock, const std::string& name) {
CHECK(lock);
CHECK(lock->lock_mode() == LOCK_EX);
- if (!EnsureImageManager()) return false;
+ if (!EnsureImageManager()) return Return::Error();
SnapshotStatus status;
if (!ReadSnapshotStatus(lock, name, &status)) {
- return false;
+ return Return::Error();
}
// The COW file size should have been rounded up to the nearest sector in CreateSnapshot.
@@ -304,12 +305,12 @@
if (status.cow_file_size() % kSectorSize != 0) {
LOG(ERROR) << "Snapshot " << name << " COW file size is not a multiple of the sector size: "
<< status.cow_file_size();
- return false;
+ return Return::Error();
}
std::string cow_image_name = GetCowImageDeviceName(name);
int cow_flags = IImageManager::CREATE_IMAGE_DEFAULT;
- return images_->CreateBackingImage(cow_image_name, status.cow_file_size(), cow_flags);
+ return Return(images_->CreateBackingImage(cow_image_name, status.cow_file_size(), cow_flags));
}
bool SnapshotManager::MapSnapshot(LockedFile* lock, const std::string& name,
@@ -559,9 +560,26 @@
}
}
+ DmTargetSnapshot::Status initial_target_values = {};
+ for (const auto& snapshot : snapshots) {
+ DmTargetSnapshot::Status current_status;
+ if (!QuerySnapshotStatus(snapshot, nullptr, ¤t_status)) {
+ return false;
+ }
+ 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;
+ }
+
+ SnapshotUpdateStatus initial_status;
+ initial_status.set_state(UpdateState::Merging);
+ initial_status.set_sectors_allocated(initial_target_values.sectors_allocated);
+ initial_status.set_total_sectors(initial_target_values.total_sectors);
+ initial_status.set_metadata_sectors(initial_target_values.metadata_sectors);
+
// Point of no return - mark that we're starting a merge. From now on every
// snapshot must be a merge target.
- if (!WriteUpdateState(lock.get(), UpdateState::Merging)) {
+ if (!WriteSnapshotUpdateStatus(lock.get(), initial_status)) {
return false;
}
@@ -810,7 +828,8 @@
cancelled = true;
break;
default:
- LOG(ERROR) << "Unknown merge status: " << static_cast<uint32_t>(snapshot_state);
+ LOG(ERROR) << "Unknown merge status for \"" << snapshot << "\": "
+ << "\"" << snapshot_state << "\"";
failed = true;
break;
}
@@ -1200,7 +1219,8 @@
if (!UnmapPartitionWithSnapshot(lock, name) || !DeleteSnapshot(lock, name)) {
// Remember whether or not we were able to unmap the cow image.
auto cow_image_device = GetCowImageDeviceName(name);
- has_mapped_cow_images |= images_->IsImageMapped(cow_image_device);
+ has_mapped_cow_images |=
+ (EnsureImageManager() && images_->IsImageMapped(cow_image_device));
ok = false;
}
@@ -1231,15 +1251,45 @@
return UpdateState::None;
}
- auto state = ReadUpdateState(lock.get());
- if (progress) {
- *progress = 0.0;
- if (state == UpdateState::Merging) {
- // :TODO: When merging is implemented, set progress_val.
- } else if (state == UpdateState::MergeCompleted) {
- *progress = 100.0;
- }
+ SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock.get());
+ auto state = update_status.state();
+ if (progress == nullptr) {
+ return state;
}
+
+ if (state == UpdateState::MergeCompleted) {
+ *progress = 100.0;
+ return state;
+ }
+
+ *progress = 0.0;
+ if (state != UpdateState::Merging) {
+ return state;
+ }
+
+ // Sum all the snapshot states as if the system consists of a single huge
+ // snapshots device, then compute the merge completion percentage of that
+ // device.
+ std::vector<std::string> snapshots;
+ if (!ListSnapshots(lock.get(), &snapshots)) {
+ LOG(ERROR) << "Could not list snapshots";
+ return state;
+ }
+
+ DmTargetSnapshot::Status fake_snapshots_status = {};
+ for (const auto& snapshot : snapshots) {
+ DmTargetSnapshot::Status current_status;
+
+ if (!QuerySnapshotStatus(snapshot, nullptr, ¤t_status)) continue;
+
+ fake_snapshots_status.sectors_allocated += current_status.sectors_allocated;
+ fake_snapshots_status.total_sectors += current_status.total_sectors;
+ fake_snapshots_status.metadata_sectors += current_status.metadata_sectors;
+ }
+
+ *progress = DmTargetSnapshot::MergePercent(fake_snapshots_status,
+ update_status.sectors_allocated());
+
return state;
}
@@ -1642,15 +1692,7 @@
return OpenLock(LOCK_EX);
}
-UpdateState SnapshotManager::ReadUpdateState(LockedFile* lock) {
- CHECK(lock);
-
- std::string contents;
- if (!android::base::ReadFileToString(GetStateFilePath(), &contents)) {
- PLOG(ERROR) << "Read state file failed";
- return UpdateState::None;
- }
-
+static UpdateState UpdateStateFromString(const std::string& contents) {
if (contents.empty() || contents == "none") {
return UpdateState::None;
} else if (contents == "initiated") {
@@ -1666,7 +1708,7 @@
} else if (contents == "merge-failed") {
return UpdateState::MergeFailed;
} else {
- LOG(ERROR) << "Unknown merge state in update state file";
+ LOG(ERROR) << "Unknown merge state in update state file: \"" << contents << "\"";
return UpdateState::None;
}
}
@@ -1688,23 +1730,59 @@
case UpdateState::MergeFailed:
return os << "merge-failed";
default:
- LOG(ERROR) << "Unknown update state";
+ LOG(ERROR) << "Unknown update state: " << static_cast<uint32_t>(state);
return os;
}
}
+UpdateState SnapshotManager::ReadUpdateState(LockedFile* lock) {
+ SnapshotUpdateStatus status = ReadSnapshotUpdateStatus(lock);
+ return status.state();
+}
+
+SnapshotUpdateStatus SnapshotManager::ReadSnapshotUpdateStatus(LockedFile* lock) {
+ CHECK(lock);
+
+ SnapshotUpdateStatus status = {};
+ std::string contents;
+ if (!android::base::ReadFileToString(GetStateFilePath(), &contents)) {
+ PLOG(ERROR) << "Read state file failed";
+ status.set_state(UpdateState::None);
+ return status;
+ }
+
+ if (!status.ParseFromString(contents)) {
+ LOG(WARNING) << "Unable to parse state file as SnapshotUpdateStatus, using the old format";
+
+ // Try to rollback to legacy file to support devices that are
+ // currently using the old file format.
+ // TODO(b/147409432)
+ status.set_state(UpdateStateFromString(contents));
+ }
+
+ return status;
+}
+
bool SnapshotManager::WriteUpdateState(LockedFile* lock, UpdateState state) {
+ SnapshotUpdateStatus status = {};
+ status.set_state(state);
+ return WriteSnapshotUpdateStatus(lock, status);
+}
+
+bool SnapshotManager::WriteSnapshotUpdateStatus(LockedFile* lock,
+ const SnapshotUpdateStatus& status) {
CHECK(lock);
CHECK(lock->lock_mode() == LOCK_EX);
- std::stringstream ss;
- ss << state;
- std::string contents = ss.str();
- if (contents.empty()) return false;
+ std::string contents;
+ if (!status.SerializeToString(&contents)) {
+ LOG(ERROR) << "Unable to serialize SnapshotUpdateStatus.";
+ return false;
+ }
#ifdef LIBSNAPSHOT_USE_HAL
auto merge_status = MergeStatus::UNKNOWN;
- switch (state) {
+ switch (status.state()) {
// The needs-reboot and completed cases imply that /data and /metadata
// can be safely wiped, so we don't report a merge status.
case UpdateState::None:
@@ -1723,7 +1801,7 @@
default:
// Note that Cancelled flows to here - it is never written, since
// it only communicates a transient state to the caller.
- LOG(ERROR) << "Unexpected update status: " << state;
+ LOG(ERROR) << "Unexpected update status: " << status.state();
break;
}
@@ -1844,9 +1922,21 @@
}
}
-bool SnapshotManager::CreateUpdateSnapshots(const DeltaArchiveManifest& manifest) {
+static Return AddRequiredSpace(Return orig,
+ const std::map<std::string, SnapshotStatus>& all_snapshot_status) {
+ if (orig.error_code() != Return::ErrorCode::NO_SPACE) {
+ return orig;
+ }
+ uint64_t sum = 0;
+ for (auto&& [name, status] : all_snapshot_status) {
+ sum += status.cow_file_size();
+ }
+ return Return::NoSpace(sum);
+}
+
+Return SnapshotManager::CreateUpdateSnapshots(const DeltaArchiveManifest& manifest) {
auto lock = LockExclusive();
- if (!lock) return false;
+ if (!lock) return Return::Error();
// TODO(b/134949511): remove this check. Right now, with overlayfs mounted, the scratch
// partition takes up a big chunk of space in super, causing COW images to be created on
@@ -1854,7 +1944,7 @@
if (device_->IsOverlayfsSetup()) {
LOG(ERROR) << "Cannot create update snapshots with overlayfs setup. Run `adb enable-verity`"
<< ", reboot, then try again.";
- return false;
+ return Return::Error();
}
const auto& opener = device_->GetPartitionOpener();
@@ -1879,7 +1969,7 @@
SnapshotMetadataUpdater metadata_updater(target_metadata.get(), target_slot, manifest);
if (!metadata_updater.Update()) {
LOG(ERROR) << "Cannot calculate new metadata.";
- return false;
+ return Return::Error();
}
// Delete previous COW partitions in current_metadata so that PartitionCowCreator marks those as
@@ -1911,36 +2001,34 @@
.extra_extents = {},
};
- if (!CreateUpdateSnapshotsInternal(lock.get(), manifest, &cow_creator, &created_devices,
- &all_snapshot_status)) {
- return false;
- }
+ auto ret = CreateUpdateSnapshotsInternal(lock.get(), manifest, &cow_creator, &created_devices,
+ &all_snapshot_status);
+ if (!ret.is_ok()) return ret;
auto exported_target_metadata = target_metadata->Export();
if (exported_target_metadata == nullptr) {
LOG(ERROR) << "Cannot export target metadata";
- return false;
+ return Return::Error();
}
- if (!InitializeUpdateSnapshots(lock.get(), target_metadata.get(),
- exported_target_metadata.get(), target_suffix,
- all_snapshot_status)) {
- return false;
- }
+ ret = InitializeUpdateSnapshots(lock.get(), target_metadata.get(),
+ exported_target_metadata.get(), target_suffix,
+ all_snapshot_status);
+ if (!ret.is_ok()) return ret;
if (!UpdatePartitionTable(opener, device_->GetSuperDevice(target_slot),
*exported_target_metadata, target_slot)) {
LOG(ERROR) << "Cannot write target metadata";
- return false;
+ return Return::Error();
}
created_devices.Release();
LOG(INFO) << "Successfully created all snapshots for target slot " << target_suffix;
- return true;
+ return Return::Ok();
}
-bool SnapshotManager::CreateUpdateSnapshotsInternal(
+Return SnapshotManager::CreateUpdateSnapshotsInternal(
LockedFile* lock, const DeltaArchiveManifest& manifest, PartitionCowCreator* cow_creator,
AutoDeviceList* created_devices,
std::map<std::string, SnapshotStatus>* all_snapshot_status) {
@@ -1951,7 +2039,7 @@
if (!target_metadata->AddGroup(kCowGroupName, 0)) {
LOG(ERROR) << "Cannot add group " << kCowGroupName;
- return false;
+ return Return::Error();
}
std::map<std::string, const RepeatedPtrField<InstallOperation>*> install_operation_map;
@@ -1963,7 +2051,7 @@
if (!inserted) {
LOG(ERROR) << "Duplicated partition " << partition_update.partition_name()
<< " in update manifest.";
- return false;
+ return Return::Error();
}
auto& extra_extents = extra_extents_map[suffixed_name];
@@ -1992,7 +2080,7 @@
// Compute the device sizes for the partition.
auto cow_creator_ret = cow_creator->Run();
if (!cow_creator_ret.has_value()) {
- return false;
+ return Return::Error();
}
LOG(INFO) << "For partition " << target_partition->name()
@@ -2006,7 +2094,7 @@
if (!DeleteSnapshot(lock, target_partition->name())) {
LOG(ERROR) << "Cannot delete existing snapshot before creating a new one for partition "
<< target_partition->name();
- return false;
+ return Return::Error();
}
// It is possible that the whole partition uses free space in super, and snapshot / COW
@@ -2024,7 +2112,7 @@
// Store these device sizes to snapshot status file.
if (!CreateSnapshot(lock, &cow_creator_ret->snapshot_status)) {
- return false;
+ return Return::Error();
}
created_devices->EmplaceBack<AutoDeleteSnapshot>(this, lock, target_partition->name());
@@ -2038,7 +2126,7 @@
auto cow_partition = target_metadata->AddPartition(GetCowName(target_partition->name()),
kCowGroupName, 0 /* flags */);
if (cow_partition == nullptr) {
- return false;
+ return Return::Error();
}
if (!target_metadata->ResizePartition(
@@ -2046,28 +2134,34 @@
cow_creator_ret->cow_partition_usable_regions)) {
LOG(ERROR) << "Cannot create COW partition on metadata with size "
<< cow_creator_ret->snapshot_status.cow_partition_size();
- return false;
+ return Return::Error();
}
// Only the in-memory target_metadata is modified; nothing to clean up if there is an
// error in the future.
}
- // Create the backing COW image if necessary.
- if (cow_creator_ret->snapshot_status.cow_file_size() > 0) {
- if (!CreateCowImage(lock, target_partition->name())) {
- return false;
- }
- }
-
all_snapshot_status->emplace(target_partition->name(),
std::move(cow_creator_ret->snapshot_status));
- LOG(INFO) << "Successfully created snapshot for " << target_partition->name();
+ LOG(INFO) << "Successfully created snapshot partition for " << target_partition->name();
}
- return true;
+
+ LOG(INFO) << "Allocating CoW images.";
+
+ for (auto&& [name, snapshot_status] : *all_snapshot_status) {
+ // Create the backing COW image if necessary.
+ if (snapshot_status.cow_file_size() > 0) {
+ auto ret = CreateCowImage(lock, name);
+ if (!ret.is_ok()) return AddRequiredSpace(ret, *all_snapshot_status);
+ }
+
+ LOG(INFO) << "Successfully created snapshot for " << name;
+ }
+
+ return Return::Ok();
}
-bool SnapshotManager::InitializeUpdateSnapshots(
+Return SnapshotManager::InitializeUpdateSnapshots(
LockedFile* lock, MetadataBuilder* target_metadata,
const LpMetadata* exported_target_metadata, const std::string& target_suffix,
const std::map<std::string, SnapshotStatus>& all_snapshot_status) {
@@ -2086,7 +2180,7 @@
if (!UnmapPartitionWithSnapshot(lock, target_partition->name())) {
LOG(ERROR) << "Cannot unmap existing COW devices before re-mapping them for zero-fill: "
<< target_partition->name();
- return false;
+ return Return::Error();
}
auto it = all_snapshot_status.find(target_partition->name());
@@ -2094,23 +2188,24 @@
cow_params.partition_name = target_partition->name();
std::string cow_name;
if (!MapCowDevices(lock, cow_params, it->second, &created_devices_for_cow, &cow_name)) {
- return false;
+ return Return::Error();
}
std::string cow_path;
if (!dm.GetDmDevicePathByName(cow_name, &cow_path)) {
LOG(ERROR) << "Cannot determine path for " << cow_name;
- return false;
+ return Return::Error();
}
- if (!InitializeCow(cow_path)) {
+ auto ret = InitializeCow(cow_path);
+ if (!ret.is_ok()) {
LOG(ERROR) << "Can't zero-fill COW device for " << target_partition->name() << ": "
<< cow_path;
- return false;
+ return AddRequiredSpace(ret, all_snapshot_status);
}
// Let destructor of created_devices_for_cow to unmap the COW devices.
};
- return true;
+ return Return::Ok();
}
bool SnapshotManager::MapUpdateSnapshot(const CreateLogicalPartitionParams& params,
@@ -2221,9 +2316,19 @@
}
}
+ 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.";
- auto state = ProcessUpdateState();
+ auto state = ProcessUpdateState(callback);
if (state == UpdateState::None) {
LOG(INFO) << "Can't find any snapshot to merge.";
return state;
@@ -2235,14 +2340,15 @@
}
// All other states can be handled by ProcessUpdateState.
LOG(INFO) << "Waiting for merge to complete. This can take up to several minutes.";
- state = ProcessUpdateState();
+ last_progress = 0;
+ state = ProcessUpdateState(callback);
}
LOG(INFO) << "Merge finished with state \"" << state << "\".";
return state;
}
-bool SnapshotManager::WaitForMerge() {
+Return SnapshotManager::WaitForMerge() {
LOG(INFO) << "Waiting for any previous merge request to complete. "
<< "This can take up to several minutes.";
while (true) {
@@ -2253,7 +2359,18 @@
continue;
}
LOG(INFO) << "Wait for merge exits with state " << state;
- return state == UpdateState::None || state == UpdateState::MergeCompleted;
+ 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();
+ }
}
}
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 2da0103..47ac474 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -47,6 +47,7 @@
using android::base::unique_fd;
using android::dm::DeviceMapper;
using android::dm::DmDeviceState;
+using android::fiemap::FiemapStatus;
using android::fiemap::IImageManager;
using android::fs_mgr::BlockDeviceInfo;
using android::fs_mgr::CreateLogicalPartitionParams;
@@ -1585,6 +1586,29 @@
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>();
+ ASSERT_TRUE(userdata->Init(kMaxFree));
+
+ // Grow all partitions to 5_MiB, total 15_MiB. This requires 15 MiB of CoW space. After
+ // using the empty space in super (< 1 MiB), it uses at least 14 MiB of /userdata space.
+ constexpr uint64_t partition_size = 5_MiB;
+ SetSize(sys_, partition_size);
+ SetSize(vnd_, partition_size);
+ SetSize(prd_, partition_size);
+
+ AddOperationForPartitions();
+
+ // Execute the update.
+ ASSERT_TRUE(sm->BeginUpdate());
+ auto res = sm->CreateUpdateSnapshots(manifest_);
+ ASSERT_FALSE(res);
+ ASSERT_EQ(Return::ErrorCode::NO_SPACE, res.error_code());
+ ASSERT_GE(res.required_size(), 14_MiB);
+ ASSERT_LT(res.required_size(), 15_MiB);
+}
+
class FlashAfterUpdateTest : public SnapshotUpdateTest,
public WithParamInterface<std::tuple<uint32_t, bool>> {
public:
@@ -1700,6 +1724,55 @@
"Merge"s;
});
+// Test behavior of ImageManager::Create on low space scenario. These tests assumes image manager
+// uses /data as backup device.
+class ImageManagerTest : public SnapshotTest, public WithParamInterface<uint64_t> {
+ public:
+ void SetUp() override {
+ SnapshotTest::SetUp();
+ userdata_ = std::make_unique<LowSpaceUserdata>();
+ ASSERT_TRUE(userdata_->Init(GetParam()));
+ }
+ void TearDown() override {
+ EXPECT_TRUE(!image_manager_->BackingImageExists(kImageName) ||
+ image_manager_->DeleteBackingImage(kImageName));
+ }
+ static constexpr const char* kImageName = "my_image";
+ std::unique_ptr<LowSpaceUserdata> userdata_;
+};
+
+TEST_P(ImageManagerTest, CreateImageEnoughAvailSpace) {
+ if (userdata_->available_space() == 0) {
+ GTEST_SKIP() << "/data is full (" << userdata_->available_space()
+ << " bytes available), skipping";
+ }
+ ASSERT_TRUE(image_manager_->CreateBackingImage(kImageName, userdata_->available_space(),
+ IImageManager::CREATE_IMAGE_DEFAULT))
+ << "Should be able to create image with size = " << userdata_->available_space()
+ << " bytes";
+ ASSERT_TRUE(image_manager_->DeleteBackingImage(kImageName))
+ << "Should be able to delete created image";
+}
+
+TEST_P(ImageManagerTest, CreateImageNoSpace) {
+ uint64_t to_allocate = userdata_->free_space() + userdata_->bsize();
+ auto res = image_manager_->CreateBackingImage(kImageName, to_allocate,
+ IImageManager::CREATE_IMAGE_DEFAULT);
+ ASSERT_FALSE(res) << "Should not be able to create image with size = " << to_allocate
+ << " bytes because only " << userdata_->free_space() << " bytes are free";
+ ASSERT_EQ(FiemapStatus::ErrorCode::NO_SPACE, res.error_code()) << res.string();
+}
+
+std::vector<uint64_t> ImageManagerTestParams() {
+ std::vector<uint64_t> ret;
+ for (uint64_t size = 1_MiB; size <= 512_MiB; size *= 2) {
+ ret.push_back(size);
+ }
+ return ret;
+}
+
+INSTANTIATE_TEST_SUITE_P(ImageManagerTest, ImageManagerTest, ValuesIn(ImageManagerTestParams()));
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
index f7f25af..b036606 100644
--- a/fs_mgr/libsnapshot/test_helpers.cpp
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -14,8 +14,11 @@
#include <libsnapshot/test_helpers.h>
+#include <sys/statvfs.h>
+
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <gtest/gtest.h>
#include <openssl/sha.h>
@@ -167,5 +170,67 @@
return partition_update->mutable_new_partition_info()->size();
}
+AssertionResult LowSpaceUserdata::Init(uint64_t max_free_space) {
+ auto res = ReadUserdataStats();
+ if (!res) return res;
+
+ // Try to fill up the disk as much as possible until free_space_ <= max_free_space.
+ big_file_ = std::make_unique<TemporaryFile>();
+ if (big_file_->fd == -1) {
+ return AssertionFailure() << strerror(errno);
+ }
+ if (!android::base::StartsWith(big_file_->path, kUserDataDevice)) {
+ return AssertionFailure() << "Temp file allocated to " << big_file_->path << ", not in "
+ << kUserDataDevice;
+ }
+ uint64_t next_consume =
+ std::min(free_space_ - max_free_space, (uint64_t)std::numeric_limits<off_t>::max());
+ off_t allocated = 0;
+ while (next_consume > 0 && free_space_ > max_free_space) {
+ int status = fallocate(big_file_->fd, 0, allocated, next_consume);
+ if (status == -1 && errno == ENOSPC) {
+ next_consume /= 2;
+ continue;
+ }
+ if (status == -1) {
+ return AssertionFailure() << strerror(errno);
+ }
+ allocated += next_consume;
+
+ res = ReadUserdataStats();
+ if (!res) return res;
+ }
+
+ LOG(INFO) << allocated << " bytes allocated to " << big_file_->path;
+ initialized_ = true;
+ return AssertionSuccess();
+}
+
+AssertionResult LowSpaceUserdata::ReadUserdataStats() {
+ struct statvfs buf;
+ if (statvfs(kUserDataDevice, &buf) == -1) {
+ return AssertionFailure() << strerror(errno);
+ }
+ bsize_ = buf.f_bsize;
+ free_space_ = buf.f_bsize * buf.f_bfree;
+ available_space_ = buf.f_bsize * buf.f_bavail;
+ return AssertionSuccess();
+}
+
+uint64_t LowSpaceUserdata::free_space() const {
+ CHECK(initialized_);
+ return free_space_;
+}
+
+uint64_t LowSpaceUserdata::available_space() const {
+ CHECK(initialized_);
+ return available_space_;
+}
+
+uint64_t LowSpaceUserdata::bsize() const {
+ CHECK(initialized_);
+ return bsize_;
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/utility.cpp b/fs_mgr/libsnapshot/utility.cpp
index fa1d7f0..3a64448 100644
--- a/fs_mgr/libsnapshot/utility.cpp
+++ b/fs_mgr/libsnapshot/utility.cpp
@@ -14,12 +14,15 @@
#include "utility.h"
+#include <errno.h>
+
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/strings.h>
#include <fs_mgr/roots.h>
using android::dm::kSectorSize;
+using android::fiemap::FiemapStatus;
using android::fs_mgr::EnsurePathMounted;
using android::fs_mgr::EnsurePathUnmounted;
using android::fs_mgr::Fstab;
@@ -83,7 +86,7 @@
}
}
-bool InitializeCow(const std::string& device) {
+Return InitializeCow(const std::string& device) {
// When the kernel creates a persistent dm-snapshot, it requires a CoW file
// to store the modifications. The kernel interface does not specify how
// the CoW is used, and there is no standard associated.
@@ -103,15 +106,15 @@
android::base::unique_fd fd(open(device.c_str(), O_WRONLY | O_BINARY));
if (fd < 0) {
PLOG(ERROR) << "Can't open COW device: " << device;
- return false;
+ return Return(FiemapStatus::FromErrno(errno));
}
LOG(INFO) << "Zero-filling COW device: " << device;
if (!android::base::WriteFully(fd, zeros.data(), kDmSnapZeroFillSize)) {
PLOG(ERROR) << "Can't zero-fill COW device for " << device;
- return false;
+ return Return(FiemapStatus::FromErrno(errno));
}
- return true;
+ return Return::Ok();
}
std::unique_ptr<AutoUnmountDevice> AutoUnmountDevice::New(const std::string& path) {
diff --git a/fs_mgr/libsnapshot/utility.h b/fs_mgr/libsnapshot/utility.h
index 5cc572e..ad46090 100644
--- a/fs_mgr/libsnapshot/utility.h
+++ b/fs_mgr/libsnapshot/utility.h
@@ -26,6 +26,7 @@
#include <update_engine/update_metadata.pb.h>
#include <libsnapshot/auto_device.h>
+#include <libsnapshot/snapshot.h>
namespace android {
namespace snapshot {
@@ -110,7 +111,7 @@
android::fs_mgr::MetadataBuilder* builder, const std::string& suffix);
// Initialize a device before using it as the COW device for a dm-snapshot device.
-bool InitializeCow(const std::string& device);
+Return InitializeCow(const std::string& device);
// "Atomically" write string to file. This is done by a series of actions:
// 1. Write to path + ".tmp"
diff --git a/fs_mgr/libvbmeta/Android.bp b/fs_mgr/libvbmeta/Android.bp
index 937e0f3..bceabab 100644
--- a/fs_mgr/libvbmeta/Android.bp
+++ b/fs_mgr/libvbmeta/Android.bp
@@ -37,6 +37,7 @@
cc_test_host {
name: "libvbmeta_test",
static_libs: [
+ "liblog",
"libsparse",
"libvbmeta",
"libz",
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index bdf4aac..7caf468 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -126,6 +126,26 @@
return *ret;
}
+BatteryCapacityLevel getBatteryCapacityLevel(const char* capacityLevel) {
+ static SysfsStringEnumMap<BatteryCapacityLevel> batteryCapacityLevelMap[] = {
+ {"Unknown", BatteryCapacityLevel::UNKNOWN},
+ {"Critical", BatteryCapacityLevel::CRITICAL},
+ {"Low", BatteryCapacityLevel::LOW},
+ {"Normal", BatteryCapacityLevel::NORMAL},
+ {"High", BatteryCapacityLevel::HIGH},
+ {"Full", BatteryCapacityLevel::FULL},
+ {NULL, BatteryCapacityLevel::UNKNOWN},
+ };
+
+ auto ret = mapSysfsString(capacityLevel, batteryCapacityLevelMap);
+ if (!ret) {
+ KLOG_WARNING(LOG_TAG, "Unknown battery capacity level '%s'\n", capacityLevel);
+ *ret = BatteryCapacityLevel::UNKNOWN;
+ }
+
+ return *ret;
+}
+
BatteryHealth getBatteryHealth(const char* status) {
static SysfsStringEnumMap<BatteryHealth> batteryHealthMap[] = {
{"Unknown", BatteryHealth::UNKNOWN},
@@ -241,9 +261,10 @@
mHealthInfo->legacy.batteryCurrentAverage =
getIntField(mHealthdConfig->batteryCurrentAvgPath);
- // TODO(b/142260281): Retrieve these values correctly.
- mHealthInfo->batteryCapacityLevel = BatteryCapacityLevel::UNKNOWN;
- mHealthInfo->batteryChargeTimeToFullNowSeconds = 0;
+ if (!mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty())
+ mHealthInfo->batteryChargeTimeToFullNowSeconds =
+ getIntField(mHealthdConfig->batteryChargeTimeToFullNowPath);
+
mHealthInfo->batteryFullCapacityUah = props.batteryFullCharge;
props.batteryTemperature = mBatteryFixedTemperature ?
@@ -252,6 +273,9 @@
std::string buf;
+ if (readFromFile(mHealthdConfig->batteryCapacityLevelPath, &buf) > 0)
+ mHealthInfo->batteryCapacityLevel = getBatteryCapacityLevel(buf.c_str());
+
if (readFromFile(mHealthdConfig->batteryStatusPath, &buf) > 0)
props.batteryStatus = getBatteryStatus(buf.c_str());
@@ -585,6 +609,19 @@
mHealthdConfig->batteryCycleCountPath = path;
}
+ if (mHealthdConfig->batteryCapacityLevelPath.isEmpty()) {
+ path.clear();
+ path.appendFormat("%s/%s/capacity_level", POWER_SUPPLY_SYSFS_PATH, name);
+ if (access(path, R_OK) == 0) mHealthdConfig->batteryCapacityLevelPath = path;
+ }
+
+ if (mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty()) {
+ path.clear();
+ path.appendFormat("%s/%s/time_to_full_now", POWER_SUPPLY_SYSFS_PATH, name);
+ if (access(path, R_OK) == 0)
+ mHealthdConfig->batteryChargeTimeToFullNowPath = path;
+ }
+
if (mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
path.clear();
path.appendFormat("%s/%s/current_avg",
@@ -653,6 +690,10 @@
KLOG_WARNING(LOG_TAG, "BatteryFullChargePath not found\n");
if (mHealthdConfig->batteryCycleCountPath.isEmpty())
KLOG_WARNING(LOG_TAG, "BatteryCycleCountPath not found\n");
+ if (mHealthdConfig->batteryCapacityLevelPath.isEmpty())
+ KLOG_WARNING(LOG_TAG, "batteryCapacityLevelPath not found\n");
+ if (mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty())
+ KLOG_WARNING(LOG_TAG, "batteryChargeTimeToFullNowPath. not found\n");
}
if (property_get("ro.boot.fake_battery", pval, NULL) > 0
diff --git a/healthd/include/healthd/healthd.h b/healthd/include/healthd/healthd.h
index a900071..8ffb114 100644
--- a/healthd/include/healthd/healthd.h
+++ b/healthd/include/healthd/healthd.h
@@ -69,6 +69,8 @@
android::String8 batteryChargeCounterPath;
android::String8 batteryFullChargePath;
android::String8 batteryCycleCountPath;
+ android::String8 batteryCapacityLevelPath;
+ android::String8 batteryChargeTimeToFullNowPath;
int (*energyCounter)(int64_t *);
int boot_min_cap;
diff --git a/init/README.md b/init/README.md
index b8300fa..4f0a7ec 100644
--- a/init/README.md
+++ b/init/README.md
@@ -487,6 +487,25 @@
-f: force installation of the module even if the version of the running kernel
and the version of the kernel for which the module was compiled do not match.
+`interface_start <name>` \
+`interface_restart <name>` \
+`interface_stop <name>`
+> Find the service that provides the interface _name_ if it exists and run the `start`, `restart`,
+or `stop` commands on it respectively. _name_ may be either a fully qualified HIDL name, in which
+case it is specified as `<interface>/<instance>`, or an AIDL name, in which case it is specified as
+`aidl/<interface>` for example `android.hardware.secure_element@1.1::ISecureElement/eSE1` or
+`aidl/aidl_lazy_test_1`.
+
+> Note that these commands only act on interfaces specified by the `interface` service option, not
+on interfaces registered at runtime.
+
+> Example usage of these commands: \
+`interface_start android.hardware.secure_element@1.1::ISecureElement/eSE1`
+will start the HIDL Service that provides the `android.hardware.secure_element@1.1` and `eSI1`
+instance. \
+`interface_start aidl/aidl_lazy_test_1` will start the AIDL service that
+provides the `aidl_lazy_test_1` interface.
+
`load_system_props`
> (This action is deprecated and no-op.)
@@ -700,6 +719,26 @@
`/sys/fs/ext4/${dev.mnt.blk.<mount_point>}/` to tune the block device
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
+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
+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.
+
+`ctl.sigstop_on` and `ctl.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.
Boot timing
-----------
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 2a6df84..c877590 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -1119,19 +1119,28 @@
}
static Result<void> ExecVdcRebootOnFailure(const std::string& vdc_arg) {
+ bool should_reboot_into_recovery = true;
auto reboot_reason = vdc_arg + "_failed";
+ if (android::sysprop::InitProperties::userspace_reboot_in_progress().value_or(false)) {
+ should_reboot_into_recovery = false;
+ }
- auto reboot = [reboot_reason](const std::string& message) {
+ auto reboot = [reboot_reason, should_reboot_into_recovery](const std::string& message) {
// TODO (b/122850122): support this in gsi
- if (fscrypt_is_native() && !android::gsi::IsGsiRunning()) {
- LOG(ERROR) << message << ": Rebooting into recovery, reason: " << reboot_reason;
- if (auto result = reboot_into_recovery(
- {"--prompt_and_wipe_data", "--reason="s + reboot_reason});
- !result) {
- LOG(FATAL) << "Could not reboot into recovery: " << result.error();
+ if (should_reboot_into_recovery) {
+ if (fscrypt_is_native() && !android::gsi::IsGsiRunning()) {
+ LOG(ERROR) << message << ": Rebooting into recovery, reason: " << reboot_reason;
+ if (auto result = reboot_into_recovery(
+ {"--prompt_and_wipe_data", "--reason="s + reboot_reason});
+ !result) {
+ LOG(FATAL) << "Could not reboot into recovery: " << result.error();
+ }
+ } else {
+ LOG(ERROR) << "Failure (reboot suppressed): " << reboot_reason;
}
} else {
- LOG(ERROR) << "Failure (reboot suppressed): " << reboot_reason;
+ LOG(ERROR) << message << ": rebooting, reason: " << reboot_reason;
+ trigger_shutdown("reboot," + reboot_reason);
}
};
@@ -1267,7 +1276,7 @@
if (strchr(name, '@') != nullptr) continue;
auto path = "/data/misc/apexdata/" + std::string(name);
- auto options = MkdirOptions{path, 0770, AID_ROOT, AID_SYSTEM, FscryptAction::kNone, "ref"};
+ auto options = MkdirOptions{path, 0771, AID_ROOT, AID_SYSTEM, FscryptAction::kNone, "ref"};
make_dir_with_options(options);
}
return {};
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index e5e99e1..9da32e4 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -58,7 +58,6 @@
using android::fs_mgr::ReadFstabFromDt;
using android::fs_mgr::SkipMountingPartitions;
using android::fs_mgr::TransformFstabForDsu;
-using android::init::WriteFile;
using android::snapshot::SnapshotManager;
using namespace std::literals;
@@ -558,6 +557,14 @@
continue;
}
+ // Skip raw partition entries such as boot, dtbo, etc.
+ // Having emmc fstab entries allows us to probe current->vbmeta_partition
+ // in InitDevices() when they are AVB chained partitions.
+ if (current->fs_type == "emmc") {
+ ++current;
+ continue;
+ }
+
Fstab::iterator end;
if (!MountPartition(current, false /* erase_same_mounts */, &end)) {
if (current->fs_mgr_flags.no_fail) {
@@ -612,7 +619,13 @@
}
return InitRequiredDevices(std::move(devices));
};
- auto images = IImageManager::Open("dsu", 0ms);
+ std::string active_dsu;
+ if (!gsi::GetActiveDsu(&active_dsu)) {
+ LOG(ERROR) << "Failed to GetActiveDsu";
+ return;
+ }
+ LOG(INFO) << "DSU slot: " << active_dsu;
+ auto images = IImageManager::Open("dsu/" + active_dsu, 0ms);
if (!images || !images->MapAllImages(init_devices)) {
LOG(ERROR) << "DSU partition layout could not be instantiated";
return;
diff --git a/init/init.cpp b/init/init.cpp
index 5f97e44..a25bf6c 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -730,8 +730,8 @@
}
am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
-
am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
+ am.QueueBuiltinAction(TestPerfEventSelinuxAction, "TestPerfEventSelinux");
am.QueueEventTrigger("early-init");
// Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index 93eb244..1a474fb 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -35,6 +35,19 @@
namespace init {
namespace {
+static bool BindMount(const std::string& source, const std::string& mount_point,
+ bool recursive = false) {
+ unsigned long mountflags = MS_BIND;
+ if (recursive) {
+ mountflags |= MS_REC;
+ }
+ if (mount(source.c_str(), mount_point.c_str(), nullptr, mountflags, nullptr) == -1) {
+ PLOG(ERROR) << "Failed to bind mount " << source;
+ return false;
+ }
+ return true;
+}
+
static bool MakeShared(const std::string& mount_point, bool recursive = false) {
unsigned long mountflags = MS_SHARED;
if (recursive) {
@@ -47,6 +60,18 @@
return true;
}
+static bool MakeSlave(const std::string& mount_point, bool recursive = false) {
+ unsigned long mountflags = MS_SLAVE;
+ if (recursive) {
+ mountflags |= MS_REC;
+ }
+ if (mount(nullptr, mount_point.c_str(), nullptr, mountflags, nullptr) == -1) {
+ PLOG(ERROR) << "Failed to change propagation type to slave";
+ return false;
+ }
+ return true;
+}
+
static bool MakePrivate(const std::string& mount_point, bool recursive = false) {
unsigned long mountflags = MS_PRIVATE;
if (recursive) {
@@ -82,7 +107,7 @@
}
static Result<void> MountDir(const std::string& path, const std::string& mount_path) {
- if (int ret = mkdir(mount_path.c_str(), 0755); ret != 0 && ret != EEXIST) {
+ if (int ret = mkdir(mount_path.c_str(), 0755); ret != 0 && errno != EEXIST) {
return ErrnoError() << "Could not create mount point " << mount_path;
}
if (mount(path.c_str(), mount_path.c_str(), nullptr, MS_BIND, nullptr) != 0) {
@@ -191,6 +216,39 @@
// namespace
if (!(MakePrivate("/linkerconfig"))) return false;
+ // The two mount namespaces present challenges for scoped storage, because
+ // vold, which is responsible for most of the mounting, lives in the
+ // bootstrap mount namespace, whereas most other daemons and all apps live
+ // in the default namespace. Scoped storage has a need for a
+ // /mnt/installer view that is a slave bind mount of /mnt/user - in other
+ // words, all mounts under /mnt/user should automatically show up under
+ // /mnt/installer. However, additional mounts done under /mnt/installer
+ // should not propagate back to /mnt/user. In a single mount namespace
+ // this is easy to achieve, by simply marking the /mnt/installer a slave
+ // bind mount. Unfortunately, if /mnt/installer is only created and
+ // bind mounted after the two namespaces are created below, we end up
+ // with the following situation:
+ // /mnt/user and /mnt/installer share the same peer group in both the
+ // bootstrap and default namespaces. Marking /mnt/installer slave in either
+ // namespace means that it won't propagate events to the /mnt/installer in
+ // the other namespace, which is still something we require - vold is the
+ // one doing the mounting under /mnt/installer, and those mounts should
+ // show up in the default namespace as well.
+ //
+ // The simplest solution is to do the bind mount before the two namespaces
+ // are created: the effect is that in both namespaces, /mnt/installer is a
+ // 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.
+ if (!mkdir_recursive("/mnt/user", 0755)) return false;
+ if (!mkdir_recursive("/mnt/installer", 0755)) return false;
+ if (!(BindMount("/mnt/user", "/mnt/installer", true))) return false;
+ // First, make /mnt/installer a slave bind mount
+ if (!(MakeSlave("/mnt/installer"))) 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;
+
bootstrap_ns_fd.reset(OpenMountNamespace());
bootstrap_ns_id = GetMountNamespaceId();
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 225bc9c..4ee7188 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -860,6 +860,30 @@
am.QueueBuiltinAction(handler, "userspace-reboot");
}
+/**
+ * Check if "command" field is set in bootloader message.
+ *
+ * If "command" field is broken (contains non-printable characters prior to
+ * terminating zero), it will be zeroed.
+ *
+ * @param[in,out] boot Bootloader message (BCB) structure
+ * @return true if "command" field is already set, and false if it's empty
+ */
+static bool CommandIsPresent(bootloader_message* boot) {
+ if (boot->command[0] == '\0')
+ return false;
+
+ for (size_t i = 0; i < arraysize(boot->command); ++i) {
+ if (boot->command[i] == '\0')
+ return true;
+ if (!isprint(boot->command[i]))
+ break;
+ }
+
+ memset(boot->command, 0, sizeof(boot->command));
+ return false;
+}
+
void HandlePowerctlMessage(const std::string& command) {
unsigned int cmd = 0;
std::vector<std::string> cmd_params = Split(command, ",");
@@ -912,7 +936,7 @@
}
// Update the boot command field if it's empty, and preserve
// the other arguments in the bootloader message.
- if (boot.command[0] == '\0') {
+ if (!CommandIsPresent(&boot)) {
strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
if (std::string err; !write_bootloader_message(boot, &err)) {
LOG(ERROR) << "Failed to set bootloader message: " << err;
diff --git a/init/reboot_utils.cpp b/init/reboot_utils.cpp
index dac0cf4..485188b 100644
--- a/init/reboot_utils.cpp
+++ b/init/reboot_utils.cpp
@@ -34,12 +34,16 @@
namespace init {
static std::string init_fatal_reboot_target = "bootloader";
+static bool init_fatal_panic = false;
void SetFatalRebootTarget() {
std::string cmdline;
android::base::ReadFileToString("/proc/cmdline", &cmdline);
cmdline = android::base::Trim(cmdline);
+ const char kInitFatalPanicString[] = "androidboot.init_fatal_panic=true";
+ init_fatal_panic = cmdline.find(kInitFatalPanicString) != std::string::npos;
+
const char kRebootTargetString[] = "androidboot.init_fatal_reboot_target=";
auto start_pos = cmdline.find(kRebootTargetString);
if (start_pos == std::string::npos) {
@@ -133,6 +137,9 @@
for (size_t i = 0; i < backtrace->NumFrames(); i++) {
LOG(ERROR) << backtrace->FormatFrameData(i);
}
+ if (init_fatal_panic) {
+ _exit(signal_number);
+ }
RebootSystem(ANDROID_RB_RESTART2, init_fatal_reboot_target);
}
diff --git a/init/security.cpp b/init/security.cpp
index 586d0c7..6cbe642 100644
--- a/init/security.cpp
+++ b/init/security.cpp
@@ -18,14 +18,19 @@
#include <errno.h>
#include <fcntl.h>
+#include <linux/perf_event.h>
+#include <sys/ioctl.h>
+#include <sys/syscall.h>
#include <unistd.h>
#include <fstream>
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <android-base/unique_fd.h>
using android::base::unique_fd;
+using android::base::SetProperty;
namespace android {
namespace init {
@@ -197,5 +202,61 @@
return {};
}
+// Test for whether the kernel has SELinux hooks for the perf_event_open()
+// syscall. If the hooks are present, we can stop using the other permission
+// mechanism (perf_event_paranoid sysctl), and use only the SELinux policy to
+// control access to the syscall. The hooks are expected on all Android R
+// release kernels, but might be absent on devices that upgrade while keeping an
+// older kernel.
+//
+// There is no direct/synchronous way of finding out that a syscall failed due
+// to SELinux. Therefore we test for a combination of a success and a failure
+// that are explained by the platform's SELinux policy for the "init" domain:
+// * cpu-scoped perf_event is allowed
+// * ioctl() on the event fd is disallowed with EACCES
+//
+// Since init has CAP_SYS_ADMIN, these tests are not affected by the system-wide
+// perf_event_paranoid sysctl.
+//
+// If the SELinux hooks are detected, a special sysprop
+// (sys.init.perf_lsm_hooks) is set, which translates to a modification of
+// perf_event_paranoid (through init.rc sysprop actions).
+//
+// TODO(b/137092007): this entire test can be removed once the platform stops
+// supporting kernels that precede the perf_event_open hooks (Android common
+// kernels 4.4 and 4.9).
+Result<void> TestPerfEventSelinuxAction(const BuiltinArguments&) {
+ // Use a trivial event that will be configured, but not started.
+ struct perf_event_attr pe = {
+ .type = PERF_TYPE_SOFTWARE,
+ .size = sizeof(struct perf_event_attr),
+ .config = PERF_COUNT_SW_TASK_CLOCK,
+ .disabled = 1,
+ .exclude_kernel = 1,
+ };
+
+ // Open the above event targeting cpu 0. (EINTR not possible.)
+ unique_fd fd(static_cast<int>(syscall(__NR_perf_event_open, &pe, /*pid=*/-1,
+ /*cpu=*/0,
+ /*group_fd=*/-1, /*flags=*/0)));
+ if (fd == -1) {
+ PLOG(ERROR) << "Unexpected perf_event_open error";
+ return {};
+ }
+
+ int ioctl_ret = ioctl(fd, PERF_EVENT_IOC_RESET);
+ if (ioctl_ret != -1) {
+ // Success implies that the kernel doesn't have the hooks.
+ return {};
+ } else if (errno != EACCES) {
+ PLOG(ERROR) << "Unexpected perf_event ioctl error";
+ return {};
+ }
+
+ // Conclude that the SELinux hooks are present.
+ SetProperty("sys.init.perf_lsm_hooks", "1");
+ return {};
+}
+
} // namespace init
} // namespace android
diff --git a/init/security.h b/init/security.h
index b081a05..43c2739 100644
--- a/init/security.h
+++ b/init/security.h
@@ -29,6 +29,7 @@
Result<void> MixHwrngIntoLinuxRngAction(const BuiltinArguments&);
Result<void> SetMmapRndBitsAction(const BuiltinArguments&);
Result<void> SetKptrRestrictAction(const BuiltinArguments&);
+Result<void> TestPerfEventSelinuxAction(const BuiltinArguments&);
} // namespace init
} // namespace android
diff --git a/init/sysprop/InitProperties.sysprop b/init/sysprop/InitProperties.sysprop
index c856358..b876dc0 100644
--- a/init/sysprop/InitProperties.sysprop
+++ b/init/sysprop/InitProperties.sysprop
@@ -29,7 +29,7 @@
prop {
api_name: "is_userspace_reboot_supported"
type: Boolean
- scope: System
+ scope: Public
access: Readonly
prop_name: "ro.init.userspace_reboot.is_supported"
integer_as_bool: true
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 334364e..65af2b3 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -250,9 +250,8 @@
require_root: true,
}
-cc_test {
- name: "libcutils_test_static",
- test_suites: ["device-tests"],
+cc_defaults {
+ name: "libcutils_test_static_defaults",
defaults: ["libcutils_test_default"],
static_libs: [
"libc",
@@ -272,3 +271,16 @@
},
},
}
+
+cc_test {
+ name: "libcutils_test_static",
+ test_suites: ["device-tests"],
+ defaults: ["libcutils_test_static_defaults"],
+}
+
+cc_test {
+ name: "KernelLibcutilsTest",
+ test_suites: ["general-tests", "vts-core"],
+ defaults: ["libcutils_test_static_defaults"],
+ test_config: "KernelLibcutilsTest.xml",
+}
diff --git a/libcutils/KernelLibcutilsTest.xml b/libcutils/KernelLibcutilsTest.xml
new file mode 100644
index 0000000..e27fac6
--- /dev/null
+++ b/libcutils/KernelLibcutilsTest.xml
@@ -0,0 +1,33 @@
+<?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 libcutils_test_static.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="libcutils_test_static->/data/local/tmp/libcutils_test_static" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="libcutils_test_static" />
+ <option name="include-filter" value="*AshmemTest*" />
+ </test>
+</configuration>
diff --git a/libcutils/ashmem-dev.cpp b/libcutils/ashmem-dev.cpp
index 340572c..8c232f0 100644
--- a/libcutils/ashmem-dev.cpp
+++ b/libcutils/ashmem-dev.cpp
@@ -203,19 +203,23 @@
{
static const std::string ashmem_device_path = get_ashmem_device_path();
- int ret;
- struct stat st;
-
if (ashmem_device_path.empty()) {
return -1;
}
int fd = TEMP_FAILURE_RETRY(open(ashmem_device_path.c_str(), O_RDWR | O_CLOEXEC));
+
+ // fallback for APEX w/ use_vendor on Q, which would have still used /dev/ashmem
+ if (fd < 0) {
+ fd = TEMP_FAILURE_RETRY(open("/dev/ashmem", O_RDWR | O_CLOEXEC));
+ }
+
if (fd < 0) {
return fd;
}
- ret = TEMP_FAILURE_RETRY(fstat(fd, &st));
+ struct stat st;
+ int ret = TEMP_FAILURE_RETRY(fstat(fd, &st));
if (ret < 0) {
int save_errno = errno;
close(fd);
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index dc31b28..65c59bd 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -196,10 +196,6 @@
{ 00750, AID_ROOT, AID_SHELL, CAP_MASK_LONG(CAP_SETUID) |
CAP_MASK_LONG(CAP_SETGID),
"system/bin/simpleperf_app_runner" },
-
- // Support FIFO scheduling mode in SurfaceFlinger.
- { 00755, AID_SYSTEM, AID_GRAPHICS, CAP_MASK_LONG(CAP_SYS_NICE),
- "system/bin/surfaceflinger" },
// generic defaults
{ 00755, AID_ROOT, AID_ROOT, 0, "bin/*" },
{ 00640, AID_ROOT, AID_SHELL, 0, "fstab.*" },
diff --git a/libcutils/include/cutils/trace.h b/libcutils/include/cutils/trace.h
index 79b4b35..e12c32c 100644
--- a/libcutils/include/cutils/trace.h
+++ b/libcutils/include/cutils/trace.h
@@ -25,7 +25,6 @@
#include <sys/cdefs.h>
#include <sys/types.h>
#include <unistd.h>
-
#include <cutils/compiler.h>
__BEGIN_DECLS
@@ -89,6 +88,12 @@
#error ATRACE_TAG must be defined to be one of the tags defined in cutils/trace.h
#endif
+// Set this to 0 to revert to the old Binder-based atrace implementation.
+// This is only here in case rollbacks do not apply cleanly.
+// TODO(fmayer): Remove this once we are confident this won't need to be
+// rolled back, no later than 2020-03-01.
+#define ATRACE_SHMEM 1
+
/**
* Opens the trace file for writing and reads the property for initial tags.
* The atrace.tags.enableflags property sets the tags to trace.
@@ -116,11 +121,15 @@
* prevent tracing within the Zygote process.
*/
void atrace_set_tracing_enabled(bool enabled);
-
/**
- * Flag indicating whether setup has been completed, initialized to 0.
- * Nonzero indicates setup has completed.
- * Note: This does NOT indicate whether or not setup was successful.
+ * If !ATRACE_SHMEM:
+ * Flag indicating whether setup has been completed, initialized to 0.
+ * Nonzero indicates setup has completed.
+ * Note: This does NOT indicate whether or not setup was successful.
+ * If ATRACE_SHMEM:
+ * This is always set to false. This forces code that uses an old version
+ * of this header to always call into atrace_setup, in which we call
+ * atrace_init unconditionally.
*/
extern atomic_bool atrace_is_ready;
@@ -143,6 +152,12 @@
* This can be explicitly run to avoid setup delay on first trace function.
*/
#define ATRACE_INIT() atrace_init()
+#define ATRACE_GET_ENABLED_TAGS() atrace_get_enabled_tags()
+
+#if ATRACE_SHMEM
+void atrace_init();
+uint64_t atrace_get_enabled_tags();
+#else
static inline void atrace_init()
{
if (CC_UNLIKELY(!atomic_load_explicit(&atrace_is_ready, memory_order_acquire))) {
@@ -155,12 +170,12 @@
* It can be used as a guard condition around more expensive trace calculations.
* Every trace function calls this, which ensures atrace_init is run.
*/
-#define ATRACE_GET_ENABLED_TAGS() atrace_get_enabled_tags()
static inline uint64_t atrace_get_enabled_tags()
{
atrace_init();
return atrace_enabled_tags;
}
+#endif
/**
* Test if a given tag is currently enabled.
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index e1e8230..f9a3df9 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -129,6 +129,7 @@
#define AID_NETWORK_STACK 1073 /* network stack service */
#define AID_GSID 1074 /* GSI service daemon */
#define AID_FSVERITY_CERT 1075 /* fs-verity key ownership in keystore */
+#define AID_CREDSTORE 1076 /* identity credential manager service */
/* Changes to this file must be made in AOSP, *not* in internal branches. */
#define AID_SHELL 2000 /* adb and debug shell user */
diff --git a/libcutils/trace-container.cpp b/libcutils/trace-container.cpp
index d981f8f..c23d5e2 100644
--- a/libcutils/trace-container.cpp
+++ b/libcutils/trace-container.cpp
@@ -39,6 +39,11 @@
static pthread_mutex_t atrace_enabling_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_rwlock_t atrace_container_sock_rwlock = PTHREAD_RWLOCK_INITIALIZER;
+static void atrace_seq_number_changed(uint32_t, uint32_t seq_no) {
+ pthread_once(&atrace_once_control, atrace_init_once);
+ atomic_store_explicit(&last_sequence_number, seq_no, memory_order_relaxed);
+}
+
static bool atrace_init_container_sock()
{
pthread_rwlock_wrlock(&atrace_container_sock_rwlock);
diff --git a/libcutils/trace-dev.cpp b/libcutils/trace-dev.cpp
index bff16c1..2ee39d3 100644
--- a/libcutils/trace-dev.cpp
+++ b/libcutils/trace-dev.cpp
@@ -37,12 +37,39 @@
} else {
atrace_enabled_tags = atrace_get_property();
}
+#if !ATRACE_SHMEM
atomic_store_explicit(&atrace_is_ready, true, memory_order_release);
+#endif
+}
+
+static void atrace_seq_number_changed(uint32_t prev_seq_no, uint32_t seq_no) {
+ if (!atomic_load_explicit(&atrace_is_enabled, memory_order_acquire)) {
+ return;
+ }
+
+ // Someone raced us.
+ if (!atomic_compare_exchange_strong(&last_sequence_number, &prev_seq_no, seq_no)) {
+ return;
+ }
+
+ if (CC_UNLIKELY(prev_seq_no == kSeqNoNotInit)) {
+#if defined(__BIONIC__)
+ const prop_info* new_pi = __system_property_find("debug.atrace.tags.enableflags");
+ if (new_pi) atrace_property_info = new_pi;
+#endif
+ pthread_once(&atrace_once_control, atrace_init_once);
+ }
+
+ atrace_update_tags();
}
void atrace_setup()
{
+#if ATRACE_SHMEM
+ atrace_init();
+#else
pthread_once(&atrace_once_control, atrace_init_once);
+#endif
}
void atrace_begin_body(const char* name)
diff --git a/libcutils/trace-dev.inc b/libcutils/trace-dev.inc
index e3da77b..a57a4c5 100644
--- a/libcutils/trace-dev.inc
+++ b/libcutils/trace-dev.inc
@@ -34,6 +34,11 @@
#include <log/log.h>
#include <log/log_properties.h>
+#if defined(__BIONIC__)
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+#endif
+
/**
* Maximum size of a message that can be logged to the trace buffer.
* Note this message includes a tag, the pid, and the string given as the name.
@@ -41,12 +46,57 @@
*/
#define ATRACE_MESSAGE_LENGTH 1024
-atomic_bool atrace_is_ready = ATOMIC_VAR_INIT(false);
-int atrace_marker_fd = -1;
-uint64_t atrace_enabled_tags = ATRACE_TAG_NOT_READY;
-static bool atrace_is_debuggable = false;
-static atomic_bool atrace_is_enabled = ATOMIC_VAR_INIT(true);
-static pthread_mutex_t atrace_tags_mutex = PTHREAD_MUTEX_INITIALIZER;
+constexpr uint32_t kSeqNoNotInit = static_cast<uint32_t>(-1);
+
+atomic_bool atrace_is_ready = ATOMIC_VAR_INIT(false);
+int atrace_marker_fd = -1;
+uint64_t atrace_enabled_tags = ATRACE_TAG_NOT_READY;
+static bool atrace_is_debuggable = false;
+static atomic_bool atrace_is_enabled = ATOMIC_VAR_INIT(true);
+static pthread_mutex_t atrace_tags_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/**
+ * Sequence number of debug.atrace.tags.enableflags the last time the enabled
+ * tags were reloaded.
+ **/
+static _Atomic(uint32_t) last_sequence_number = ATOMIC_VAR_INIT(kSeqNoNotInit);
+
+#if defined(__BIONIC__)
+// All zero prop_info that has a sequence number of 0. This is easier than
+// depending on implementation details of the property implementation.
+//
+// prop_info is static_assert-ed to be 96 bytes, which cannot change due to
+// ABI compatibility.
+alignas(uint64_t) static char empty_pi[96];
+static const prop_info* atrace_property_info = reinterpret_cast<const prop_info*>(empty_pi);
+#endif
+
+#if ATRACE_SHMEM
+
+/**
+ * This is called when the sequence number of debug.atrace.tags.enableflags
+ * changes and we need to reload the enabled tags.
+ **/
+static void atrace_seq_number_changed(uint32_t prev_seq_no, uint32_t seq_no);
+
+void atrace_init() {
+#if defined(__BIONIC__)
+ uint32_t seq_no = __system_property_serial(atrace_property_info); // Acquire semantics.
+#else
+ uint32_t seq_no = 0;
+#endif
+ uint32_t prev_seq_no = atomic_load_explicit(&last_sequence_number, memory_order_relaxed);
+ if (CC_UNLIKELY(seq_no != prev_seq_no)) {
+ atrace_seq_number_changed(prev_seq_no, seq_no);
+ }
+}
+
+uint64_t atrace_get_enabled_tags()
+{
+ atrace_init();
+ return atrace_enabled_tags;
+}
+#endif
// Set whether this process is debuggable, which determines whether
// application-level tracing is allowed when the ro.debuggable system property
@@ -136,7 +186,7 @@
void atrace_update_tags()
{
uint64_t tags;
- if (CC_UNLIKELY(atomic_load_explicit(&atrace_is_ready, memory_order_acquire))) {
+ if (ATRACE_SHMEM || CC_UNLIKELY(atomic_load_explicit(&atrace_is_ready, memory_order_acquire))) {
if (atomic_load_explicit(&atrace_is_enabled, memory_order_acquire)) {
tags = atrace_get_property();
pthread_mutex_lock(&atrace_tags_mutex);
diff --git a/libcutils/trace-host.cpp b/libcutils/trace-host.cpp
index d47cc18..c21d0ee 100644
--- a/libcutils/trace-host.cpp
+++ b/libcutils/trace-host.cpp
@@ -30,3 +30,10 @@
void atrace_async_end_body(const char* /*name*/, int32_t /*cookie*/) {}
void atrace_int_body(const char* /*name*/, int32_t /*value*/) {}
void atrace_int64_body(const char* /*name*/, int64_t /*value*/) {}
+#if ATRACE_SHMEM
+void atrace_init() {}
+uint64_t atrace_get_enabled_tags()
+{
+ return ATRACE_TAG_NOT_READY;
+}
+#endif
diff --git a/liblog/Android.bp b/liblog/Android.bp
index bab57c0..58c96b6 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -103,7 +103,7 @@
stubs: {
symbol_file: "liblog.map.txt",
- versions: ["10000"],
+ versions: ["29", "30"],
},
// TODO(tomcherry): Renable this before release branch is cut
diff --git a/liblog/event_tag_map.cpp b/liblog/event_tag_map.cpp
index 2886289..51c5e60 100644
--- a/liblog/event_tag_map.cpp
+++ b/liblog/event_tag_map.cpp
@@ -36,7 +36,6 @@
#include <utils/FastStrcmp.h>
#include <utils/RWLock.h>
-#include "log_portability.h"
#include "logd_reader.h"
#define OUT_TAG "EventTagMap"
diff --git a/liblog/fake_log_device.cpp b/liblog/fake_log_device.cpp
index 2ec6393..cd4c11e 100644
--- a/liblog/fake_log_device.cpp
+++ b/liblog/fake_log_device.cpp
@@ -36,7 +36,6 @@
#include <log/log_id.h>
#include <log/logprint.h>
-#include "log_portability.h"
#include "logger.h"
#define kMaxTagLen 16 /* from the long-dead utils/Log.cpp */
@@ -49,14 +48,6 @@
#define TRACE(...) ((void)0)
#endif
-static void FakeClose();
-static int FakeWrite(log_id_t log_id, struct timespec* ts, struct iovec* vec, size_t nr);
-
-struct android_log_transport_write fakeLoggerWrite = {
- .close = FakeClose,
- .write = FakeWrite,
-};
-
typedef struct LogState {
bool initialized = false;
/* global minimum priority */
@@ -453,7 +444,7 @@
* tag (N bytes -- null-terminated ASCII string)
* message (N bytes -- null-terminated ASCII string)
*/
-static int FakeWrite(log_id_t log_id, struct timespec*, struct iovec* vector, size_t count) {
+int FakeWrite(log_id_t log_id, struct timespec*, struct iovec* vector, size_t count) {
/* Make sure that no-one frees the LogState while we're using it.
* Also guarantees that only one thread is in showLog() at a given
* time (if it matters).
@@ -519,20 +510,22 @@
* call is in the exit handler. Logging can continue in the exit handler to
* help debug HOST tools ...
*/
-static void FakeClose() {
+void FakeClose() {
auto lock = std::lock_guard{*fake_log_mutex};
memset(&log_state, 0, sizeof(log_state));
}
-int __android_log_is_loggable(int prio, const char*, int def) {
- int logLevel = def;
- return logLevel >= 0 && prio >= logLevel;
+int __android_log_is_loggable(int prio, const char*, int) {
+ int minimum_priority = __android_log_get_minimum_priority();
+ if (minimum_priority == ANDROID_LOG_DEFAULT) {
+ minimum_priority = ANDROID_LOG_INFO;
+ }
+ return prio >= minimum_priority;
}
int __android_log_is_loggable_len(int prio, const char*, size_t, int def) {
- int logLevel = def;
- return logLevel >= 0 && prio >= logLevel;
+ return __android_log_is_loggable(prio, nullptr, def);
}
int __android_log_is_debuggable() {
diff --git a/liblog/fake_log_device.h b/liblog/fake_log_device.h
index bd2256c..53f1b41 100644
--- a/liblog/fake_log_device.h
+++ b/liblog/fake_log_device.h
@@ -16,18 +16,15 @@
#pragma once
+#include <sys/cdefs.h>
#include <sys/types.h>
-#include "log_portability.h"
-#include "uio.h"
-
-struct iovec;
+#include <android/log.h>
__BEGIN_DECLS
-int fakeLogOpen(const char* pathName);
-int fakeLogClose(int fd);
-ssize_t fakeLogWritev(int fd, const struct iovec* vector, int count);
+void FakeClose();
+int FakeWrite(log_id_t log_id, struct timespec* ts, struct iovec* vec, size_t nr);
int __android_log_is_loggable(int prio, const char*, int def);
int __android_log_is_loggable_len(int prio, const char*, size_t, int def);
diff --git a/liblog/include/android/log.h b/liblog/include/android/log.h
index 7290789..c84ddf7 100644
--- a/liblog/include/android/log.h
+++ b/liblog/include/android/log.h
@@ -55,6 +55,7 @@
*/
#include <stdarg.h>
+#include <stddef.h>
#ifdef __cplusplus
extern "C" {
@@ -152,6 +153,12 @@
} log_id_t;
/**
+ * 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
+
+/**
* Writes the constant string `text` to the log buffer `id`,
* with priority `prio` and tag `tag`.
*
@@ -170,6 +177,112 @@
int __android_log_buf_print(int bufID, int prio, const char* tag, const char* fmt, ...)
__attribute__((__format__(printf, 4, 5)));
+/**
+ * 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. */
+};
+
+/**
+ * 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);
+
+/**
+ * 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);
+
+/**
+ * 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);
+
+/**
+ * Sets a user defined aborter function that is called for __android_log_assert() failures.
+ */
+void __android_log_set_aborter(__android_aborter_function aborter);
+
+/**
+ * Calls the stored aborter function. This allows for other logging libraries to use the same
+ * aborter function by calling this function in liblog.
+ */
+void __android_log_call_aborter(const char* abort_message);
+
+/**
+ * Sets android_set_abort_message() on device then aborts(). This is the default aborter.
+ */
+void __android_log_default_aborter(const char* abort_message);
+
+/**
+ * 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.
+ *
+ * prio is ANDROID_LOG_VERBOSE to ANDROID_LOG_FATAL.
+ */
+int __android_log_is_loggable(int prio, const char* tag, int default_prio);
+int __android_log_is_loggable_len(int prio, const char* tag, size_t len, int default_prio);
+
+/**
+ * Sets the minimum priority that will be logged for this process.
+ *
+ * This returns the previous set minimum priority, or ANDROID_LOG_DEFAULT if none was set.
+ */
+int __android_log_set_minimum_priority(int priority);
+
+/**
+ * Gets the minimum priority that will be logged for this process. If none has been set by a
+ * previous __android_log_set_minimum_priority() call, this returns ANDROID_LOG_DEFAULT.
+ */
+int __android_log_get_minimum_priority();
+
+/**
+ * Sets the default tag if no tag is provided when writing a log message. Defaults to
+ * getprogname(). This truncates tag to the maximum log message size, though appropriate tags
+ * should be much smaller.
+ */
+void __android_log_set_default_tag(const char* tag);
+
#ifdef __cplusplus
}
#endif
diff --git a/liblog/liblog.map.txt b/liblog/liblog.map.txt
index 2dd8059..198cdae 100644
--- a/liblog/liblog.map.txt
+++ b/liblog/liblog.map.txt
@@ -54,18 +54,32 @@
__android_log_is_debuggable; # apex llndk
};
-LIBLOG_Q {
+LIBLOG_Q { # introduced=29
global:
__android_log_bswrite; # apex
__android_log_btwrite; # apex
__android_log_bwrite; # apex
__android_log_close; # apex
__android_log_security; # apex
- __android_log_security_bswrite; # apex
android_log_reset; # llndk
android_log_parser_reset; # llndk
};
+LIGLOG_R { # introduced=30
+ global:
+ __android_log_call_aborter;
+ __android_log_default_aborter;
+ __android_log_get_minimum_priority;
+ __android_log_logd_logger;
+ __android_log_security_bswrite; # apex
+ __android_log_set_aborter;
+ __android_log_set_default_tag;
+ __android_log_set_logger;
+ __android_log_set_minimum_priority;
+ __android_log_stderr_logger;
+ __android_log_write_logger_data;
+};
+
LIBLOG_PRIVATE {
global:
__android_log_pmsg_file_read;
diff --git a/liblog/log_event_list.cpp b/liblog/log_event_list.cpp
index e9f4a32..cb70d48 100644
--- a/liblog/log_event_list.cpp
+++ b/liblog/log_event_list.cpp
@@ -25,8 +25,6 @@
#include <log/log_event_list.h>
#include <private/android_logger.h>
-#include "log_portability.h"
-
#define MAX_EVENT_PAYLOAD (LOGGER_ENTRY_MAX_PAYLOAD - sizeof(int32_t))
enum ReadWriteFlag {
diff --git a/liblog/log_event_write.cpp b/liblog/log_event_write.cpp
index d04ba90..39afd0c 100644
--- a/liblog/log_event_write.cpp
+++ b/liblog/log_event_write.cpp
@@ -20,8 +20,6 @@
#include <log/log.h>
#include <log/log_event_list.h>
-#include "log_portability.h"
-
#define MAX_SUBTAG_LEN 32
int __android_log_error_write(int tag, const char* subTag, int32_t uid, const char* data,
diff --git a/liblog/log_portability.h b/liblog/log_portability.h
deleted file mode 100644
index b7279d1..0000000
--- a/liblog/log_portability.h
+++ /dev/null
@@ -1,47 +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 <unistd.h>
-
-/* possible missing definitions in sys/cdefs.h */
-
-/* DECLS */
-#ifndef __BEGIN_DECLS
-#if defined(__cplusplus)
-#define __BEGIN_DECLS extern "C" {
-#define __END_DECLS }
-#else
-#define __BEGIN_DECLS
-#define __END_DECLS
-#endif
-#endif
-
-/* possible missing definitions in unistd.h */
-
-#ifndef TEMP_FAILURE_RETRY
-/* Used to retry syscalls that can return EINTR. */
-#define TEMP_FAILURE_RETRY(exp) \
- ({ \
- __typeof__(exp) _rc; \
- do { \
- _rc = (exp); \
- } while (_rc == -1 && errno == EINTR); \
- _rc; \
- })
-#endif
diff --git a/liblog/log_time.cpp b/liblog/log_time.cpp
index 3ae250f..3fbe1cb 100644
--- a/liblog/log_time.cpp
+++ b/liblog/log_time.cpp
@@ -21,8 +21,6 @@
#include <private/android_logger.h>
-#include "log_portability.h"
-
const char log_time::default_format[] = "%m-%d %H:%M:%S.%q";
const timespec log_time::EPOCH = {0, 0};
diff --git a/liblog/logd_reader.cpp b/liblog/logd_reader.cpp
index 6865c14..82ed6b2 100644
--- a/liblog/logd_reader.cpp
+++ b/liblog/logd_reader.cpp
@@ -35,8 +35,6 @@
#include <string>
-#include <cutils/sockets.h>
-#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
#include "logger.h"
diff --git a/liblog/logd_reader.h b/liblog/logd_reader.h
index 2d032fa..68eef02 100644
--- a/liblog/logd_reader.h
+++ b/liblog/logd_reader.h
@@ -16,10 +16,10 @@
#pragma once
+#include <sys/cdefs.h>
#include <unistd.h>
#include "log/log_read.h"
-#include "log_portability.h"
__BEGIN_DECLS
diff --git a/liblog/logd_writer.cpp b/liblog/logd_writer.cpp
index 3c6eb69..67376f4 100644
--- a/liblog/logd_writer.cpp
+++ b/liblog/logd_writer.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "logd_writer.h"
+
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
@@ -32,23 +34,13 @@
#include <shared_mutex>
-#include <cutils/sockets.h>
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
-#include "log_portability.h"
#include "logger.h"
#include "rwlock.h"
#include "uio.h"
-static int LogdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr);
-static void LogdClose();
-
-struct android_log_transport_write logdLoggerWrite = {
- .close = LogdClose,
- .write = LogdWrite,
-};
-
static int logd_socket;
static RwLock logd_socket_lock;
@@ -90,7 +82,7 @@
OpenSocketLocked();
}
-static void LogdClose() {
+void LogdClose() {
auto lock = std::unique_lock{logd_socket_lock};
if (logd_socket > 0) {
close(logd_socket);
@@ -98,7 +90,7 @@
logd_socket = 0;
}
-static int LogdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr) {
+int LogdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr) {
ssize_t ret;
static const unsigned headerLength = 1;
struct iovec newVec[nr + headerLength];
@@ -119,7 +111,7 @@
}
/* logd, after initialization and priv drop */
- if (__android_log_uid() == AID_LOGD) {
+ if (getuid() == AID_LOGD) {
/*
* ignore log messages we send to ourself (logd).
* Such log messages are often generated by libraries we depend on
diff --git a/liblog/logd_writer.h b/liblog/logd_writer.h
new file mode 100644
index 0000000..41197b5
--- /dev/null
+++ b/liblog/logd_writer.h
@@ -0,0 +1,24 @@
+/*
+ * 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 <android/log.h>
+
+int LogdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr);
+void LogdClose();
diff --git a/liblog/logger.h b/liblog/logger.h
index 40d5fe5..ddff19d 100644
--- a/liblog/logger.h
+++ b/liblog/logger.h
@@ -17,22 +17,14 @@
#pragma once
#include <stdatomic.h>
+#include <sys/cdefs.h>
-#include <cutils/list.h>
#include <log/log.h>
-#include "log_portability.h"
#include "uio.h"
__BEGIN_DECLS
-struct android_log_transport_write {
- void (*close)(); /* free up resources */
- /* write log to transport, returns number of bytes propagated, or -errno */
- int (*write)(log_id_t logId, struct timespec* ts, struct iovec* vec,
- size_t nr);
-};
-
struct logger_list {
atomic_int fd;
int mode;
@@ -56,18 +48,4 @@
return reinterpret_cast<uintptr_t>(logger) & LOGGER_LOGD;
}
-/* OS specific dribs and drabs */
-
-#if defined(_WIN32)
-#include <private/android_filesystem_config.h>
-typedef uint32_t uid_t;
-static inline uid_t __android_log_uid() {
- return AID_SYSTEM;
-}
-#else
-static inline uid_t __android_log_uid() {
- return getuid();
-}
-#endif
-
__END_DECLS
diff --git a/liblog/logger_name.cpp b/liblog/logger_name.cpp
index ece0550..7d676f4 100644
--- a/liblog/logger_name.cpp
+++ b/liblog/logger_name.cpp
@@ -19,8 +19,6 @@
#include <log/log.h>
-#include "log_portability.h"
-
/* In the future, we would like to make this list extensible */
static const char* LOG_NAME[LOG_ID_MAX] = {
/* clang-format off */
diff --git a/liblog/logger_read.cpp b/liblog/logger_read.cpp
index 0d383ff..e0598de 100644
--- a/liblog/logger_read.cpp
+++ b/liblog/logger_read.cpp
@@ -27,10 +27,7 @@
#include <unistd.h>
#include <android/log.h>
-#include <cutils/list.h>
-#include <private/android_filesystem_config.h>
-#include "log_portability.h"
#include "logd_reader.h"
#include "logger.h"
#include "pmsg_reader.h"
diff --git a/liblog/logger_write.cpp b/liblog/logger_write.cpp
index d38b402..e86d9ec 100644
--- a/liblog/logger_write.cpp
+++ b/liblog/logger_write.cpp
@@ -15,7 +15,8 @@
*/
#include <errno.h>
-#include <stdatomic.h>
+#include <inttypes.h>
+#include <libgen.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
@@ -24,31 +25,38 @@
#include <android/set_abort_message.h>
#endif
+#include <shared_mutex>
+
+#include <android-base/macros.h>
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
-#include "log_portability.h"
+#include "android/log.h"
+#include "log/log_read.h"
#include "logger.h"
+#include "rwlock.h"
#include "uio.h"
+#if (FAKE_LOG_DEVICE == 0)
+#include "logd_writer.h"
+#include "pmsg_writer.h"
+#else
+#include "fake_log_device.h"
+#endif
+
+#if defined(__APPLE__)
+#include <pthread.h>
+#elif defined(__linux__) && !defined(__ANDROID__)
+#include <syscall.h>
+#elif defined(_WIN32)
+#include <windows.h>
+#endif
+
#define LOG_BUF_SIZE 1024
-#if (FAKE_LOG_DEVICE == 0)
-extern struct android_log_transport_write logdLoggerWrite;
-extern struct android_log_transport_write pmsgLoggerWrite;
-
-android_log_transport_write* android_log_write = &logdLoggerWrite;
-android_log_transport_write* android_log_persist_write = &pmsgLoggerWrite;
-#else
-extern android_log_transport_write fakeLoggerWrite;
-
-android_log_transport_write* android_log_write = &fakeLoggerWrite;
-android_log_transport_write* android_log_persist_write = nullptr;
-#endif
-
#if defined(__ANDROID__)
static int check_log_uid_permissions() {
- uid_t uid = __android_log_uid();
+ uid_t uid = getuid();
/* Matches clientHasLogCredentials() in logd */
if ((uid != AID_SYSTEM) && (uid != AID_ROOT) && (uid != AID_LOG)) {
@@ -92,16 +100,99 @@
* Release any logger resources. A new log write will immediately re-acquire.
*/
void __android_log_close() {
- if (android_log_write != nullptr) {
- android_log_write->close();
- }
-
- if (android_log_persist_write != nullptr) {
- android_log_persist_write->close();
- }
-
+#if (FAKE_LOG_DEVICE == 0)
+ LogdClose();
+ PmsgClose();
+#else
+ FakeClose();
+#endif
}
+#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) {
+ char path[PATH_MAX + 1];
+ DWORD result = GetModuleFileName(nullptr, path, sizeof(path) - 1);
+ if (result == 0 || result == sizeof(path) - 1) return "";
+ path[PATH_MAX - 1] = 0;
+
+ char* path_basename = basename(path);
+
+ snprintf(progname, sizeof(progname), "%s", path_basename);
+ first = false;
+ }
+
+ return progname;
+#endif
+}
+#endif
+
+// It's possible for logging to happen during static initialization before our globals are
+// initialized, so we place this std::string in a function such that it is initialized on the first
+// call.
+static std::string& GetDefaultTag() {
+ static std::string default_tag = getprogname();
+ return default_tag;
+}
+static 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;
+}
+
+int __android_log_get_minimum_priority() {
+ return minimum_log_priority;
+}
+
+#ifdef __ANDROID__
+static __android_logger_function logger_function = __android_log_logd_logger;
+#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;
+}
+
+void __android_log_default_aborter(const char* abort_message) {
+#ifdef __ANDROID__
+ android_set_abort_message(abort_message);
+#else
+ UNUSED(abort_message);
+#endif
+ abort();
+}
+
+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);
+}
+
+#ifdef __ANDROID__
static int write_to_log(log_id_t log_id, struct iovec* vec, size_t nr) {
int ret, save_errno;
struct timespec ts;
@@ -112,7 +203,6 @@
return -EINVAL;
}
-#if defined(__ANDROID__)
clock_gettime(android_log_clockid(), &ts);
if (log_id == LOG_ID_SECURITY) {
@@ -136,77 +226,137 @@
errno = save_errno;
return -EINVAL;
}
- } else {
- int prio = *static_cast<int*>(vec[0].iov_base);
- const char* tag = static_cast<const char*>(vec[1].iov_base);
- size_t len = vec[1].iov_len;
-
- if (!__android_log_is_loggable_len(prio, tag, len - 1, ANDROID_LOG_VERBOSE)) {
- errno = save_errno;
- return -EPERM;
- }
- }
-#else
- /* simulate clock_gettime(CLOCK_REALTIME, &ts); */
- {
- struct timeval tv;
- gettimeofday(&tv, NULL);
- ts.tv_sec = tv.tv_sec;
- ts.tv_nsec = tv.tv_usec * 1000;
- }
-#endif
-
- ret = 0;
-
- if (android_log_write != nullptr) {
- ssize_t retval;
- retval = android_log_write->write(log_id, &ts, vec, nr);
- if (ret >= 0) {
- ret = retval;
- }
}
- if (android_log_persist_write != nullptr) {
- android_log_persist_write->write(log_id, &ts, vec, nr);
- }
+ ret = LogdWrite(log_id, &ts, vec, nr);
+ PmsgWrite(log_id, &ts, vec, nr);
errno = save_errno;
return ret;
}
+#else
+static int write_to_log(log_id_t, struct iovec*, size_t) {
+ // Non-Android text logs should go to __android_log_stderr_logger, not here.
+ // Non-Android binary logs are always dropped.
+ return 1;
+}
+#endif
+
+// Copied from base/threads.cpp
+static 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
+}
+
+void __android_log_stderr_logger(const struct __android_logger_data* logger_data,
+ 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[] = "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;
+ char priority_char = log_characters[priority];
+ uint64_t tid = GetThreadId();
+
+ if (logger_data->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);
+ } else {
+ fprintf(stderr, "%s %c %s %5d %5" PRIu64 " %s\n",
+ logger_data->tag ? logger_data->tag : "nullptr", priority_char, timestamp, getpid(),
+ tid, 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;
+
+ struct iovec vec[3];
+ vec[0].iov_base =
+ const_cast<unsigned char*>(reinterpret_cast<const unsigned char*>(&logger_data->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;
+
+ write_to_log(static_cast<log_id_t>(buffer_id), vec, 3);
+}
int __android_log_write(int prio, const char* tag, const char* msg) {
return __android_log_buf_write(LOG_ID_MAIN, prio, tag, msg);
}
-int __android_log_buf_write(int bufID, int prio, const char* tag, const char* msg) {
- if (!tag) tag = "";
+void __android_log_write_logger_data(__android_logger_data* logger_data, const char* msg) {
+ 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 __BIONIC__
- if (prio == ANDROID_LOG_FATAL) {
+ if (logger_data->priority == ANDROID_LOG_FATAL) {
android_set_abort_message(msg);
}
#endif
- struct iovec vec[3];
- vec[0].iov_base = (unsigned char*)&prio;
- vec[0].iov_len = 1;
- vec[1].iov_base = (void*)tag;
- vec[1].iov_len = strlen(tag) + 1;
- vec[2].iov_base = (void*)msg;
- vec[2].iov_len = strlen(msg) + 1;
+ auto lock = std::shared_lock{logger_function_lock};
+ logger_function(logger_data, msg);
+}
- return write_to_log(static_cast<log_id_t>(bufID), vec, 3);
+int __android_log_buf_write(int bufID, int prio, const char* tag, const char* msg) {
+ if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
+ return 0;
+ }
+
+ __android_logger_data logger_data = {sizeof(__android_logger_data), bufID, prio, tag, nullptr, 0};
+ __android_log_write_logger_data(&logger_data, msg);
+ return 1;
}
int __android_log_vprint(int prio, const char* tag, const char* fmt, va_list ap) {
+ if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
+ return 0;
+ }
+
char buf[LOG_BUF_SIZE];
vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
- return __android_log_write(prio, tag, buf);
+ __android_logger_data logger_data = {
+ sizeof(__android_logger_data), LOG_ID_MAIN, prio, tag, nullptr, 0};
+ __android_log_write_logger_data(&logger_data, buf);
+ return 1;
}
int __android_log_print(int prio, const char* tag, const char* fmt, ...) {
+ if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
+ return 0;
+ }
+
va_list ap;
char buf[LOG_BUF_SIZE];
@@ -214,10 +364,17 @@
vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
va_end(ap);
- return __android_log_write(prio, tag, buf);
+ __android_logger_data logger_data = {
+ sizeof(__android_logger_data), LOG_ID_MAIN, prio, tag, nullptr, 0};
+ __android_log_write_logger_data(&logger_data, buf);
+ return 1;
}
int __android_log_buf_print(int bufID, int prio, const char* tag, const char* fmt, ...) {
+ if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
+ return 0;
+ }
+
va_list ap;
char buf[LOG_BUF_SIZE];
@@ -225,7 +382,9 @@
vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
va_end(ap);
- return __android_log_buf_write(bufID, prio, tag, buf);
+ __android_logger_data logger_data = {sizeof(__android_logger_data), bufID, prio, tag, nullptr, 0};
+ __android_log_write_logger_data(&logger_data, buf);
+ return 1;
}
void __android_log_assert(const char* cond, const char* tag, const char* fmt, ...) {
@@ -253,8 +412,8 @@
TEMP_FAILURE_RETRY(write(2, "\n", 1));
__android_log_write(ANDROID_LOG_FATAL, tag, buf);
- abort(); /* abort so we have a chance to debug the situation */
- /* NOTREACHED */
+ __android_log_call_aborter(buf);
+ abort();
}
int __android_log_bwrite(int32_t tag, const void* payload, size_t len) {
diff --git a/liblog/logprint.cpp b/liblog/logprint.cpp
index 4b61828..0745a1e 100644
--- a/liblog/logprint.cpp
+++ b/liblog/logprint.cpp
@@ -40,8 +40,6 @@
#include <log/logprint.h>
#include <private/android_logger.h>
-#include "log_portability.h"
-
#define MS_PER_NSEC 1000000
#define US_PER_NSEC 1000
diff --git a/liblog/pmsg_reader.cpp b/liblog/pmsg_reader.cpp
index 9390fec..64a92b7 100644
--- a/liblog/pmsg_reader.cpp
+++ b/liblog/pmsg_reader.cpp
@@ -23,7 +23,7 @@
#include <string.h>
#include <sys/types.h>
-#include <private/android_filesystem_config.h>
+#include <cutils/list.h>
#include <private/android_logger.h>
#include "logger.h"
diff --git a/liblog/pmsg_reader.h b/liblog/pmsg_reader.h
index 53746d8..b784f9f 100644
--- a/liblog/pmsg_reader.h
+++ b/liblog/pmsg_reader.h
@@ -16,10 +16,10 @@
#pragma once
+#include <sys/cdefs.h>
#include <unistd.h>
#include "log/log_read.h"
-#include "log_portability.h"
__BEGIN_DECLS
diff --git a/liblog/pmsg_writer.cpp b/liblog/pmsg_writer.cpp
index 4f45780..06e5e04 100644
--- a/liblog/pmsg_writer.cpp
+++ b/liblog/pmsg_writer.cpp
@@ -14,9 +14,7 @@
* limitations under the License.
*/
-/*
- * pmsg write handler
- */
+#include "pmsg_writer.h"
#include <errno.h>
#include <fcntl.h>
@@ -28,22 +26,12 @@
#include <shared_mutex>
#include <log/log_properties.h>
-#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
-#include "log_portability.h"
#include "logger.h"
#include "rwlock.h"
#include "uio.h"
-static void PmsgClose();
-static int PmsgWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr);
-
-struct android_log_transport_write pmsgLoggerWrite = {
- .close = PmsgClose,
- .write = PmsgWrite,
-};
-
static int pmsg_fd;
static RwLock pmsg_fd_lock;
@@ -57,7 +45,7 @@
pmsg_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
}
-static void PmsgClose() {
+void PmsgClose() {
auto lock = std::unique_lock{pmsg_fd_lock};
if (pmsg_fd > 0) {
close(pmsg_fd);
@@ -65,7 +53,7 @@
pmsg_fd = 0;
}
-static int PmsgWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr) {
+int PmsgWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr) {
static const unsigned headerLength = 2;
struct iovec newVec[nr + headerLength];
android_log_header_t header;
@@ -123,7 +111,7 @@
pmsgHeader.magic = LOGGER_MAGIC;
pmsgHeader.len = sizeof(pmsgHeader) + sizeof(header);
- pmsgHeader.uid = __android_log_uid();
+ pmsgHeader.uid = getuid();
pmsgHeader.pid = getpid();
header.id = logId;
diff --git a/liblog/pmsg_writer.h b/liblog/pmsg_writer.h
new file mode 100644
index 0000000..d5e1a1c
--- /dev/null
+++ b/liblog/pmsg_writer.h
@@ -0,0 +1,24 @@
+/*
+ * 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 <android/log.h>
+
+int PmsgWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr);
+void PmsgClose();
diff --git a/liblog/properties.cpp b/liblog/properties.cpp
index 2e0a8c9..a53c92b 100644
--- a/liblog/properties.cpp
+++ b/liblog/properties.cpp
@@ -24,9 +24,9 @@
#include <sys/_system_properties.h>
#include <unistd.h>
-#include <private/android_logger.h>
+#include <algorithm>
-#include "log_portability.h"
+#include <private/android_logger.h>
static pthread_mutex_t lock_loggable = PTHREAD_MUTEX_INITIALIZER;
@@ -89,7 +89,7 @@
}
}
-static int __android_log_level(const char* tag, size_t len, int default_prio) {
+static int __android_log_level(const char* tag, size_t len) {
/* sizeof() is used on this array below */
static const char log_namespace[] = "persist.log.tag.";
static const size_t base_offset = 8; /* skip "persist." */
@@ -258,20 +258,30 @@
case 'F': /* FALLTHRU */ /* Not officially supported */
case 'A': return ANDROID_LOG_FATAL;
case BOOLEAN_FALSE: /* FALLTHRU */ /* Not Officially supported */
- case 'S': return -1; /* ANDROID_LOG_SUPPRESS */
+ case 'S': return ANDROID_LOG_SILENT;
/* clang-format on */
}
- return default_prio;
+ return -1;
}
int __android_log_is_loggable_len(int prio, const char* tag, size_t len, int default_prio) {
- int logLevel = __android_log_level(tag, len, default_prio);
- return logLevel >= 0 && prio >= logLevel;
+ int minimum_log_priority = __android_log_get_minimum_priority();
+ int property_log_level = __android_log_level(tag, len);
+
+ if (property_log_level >= 0 && minimum_log_priority != ANDROID_LOG_DEFAULT) {
+ return prio >= std::min(property_log_level, minimum_log_priority);
+ } else if (property_log_level >= 0) {
+ return prio >= property_log_level;
+ } else if (minimum_log_priority != ANDROID_LOG_DEFAULT) {
+ return prio >= minimum_log_priority;
+ } else {
+ return prio >= default_prio;
+ }
}
int __android_log_is_loggable(int prio, const char* tag, int default_prio) {
- int logLevel = __android_log_level(tag, (tag && *tag) ? strlen(tag) : 0, default_prio);
- return logLevel >= 0 && prio >= logLevel;
+ auto len = tag ? strlen(tag) : 0;
+ return __android_log_is_loggable_len(prio, tag, len, default_prio);
}
int __android_log_is_debuggable() {
diff --git a/liblog/tests/Android.bp b/liblog/tests/Android.bp
index f58c524..b3364f9 100644
--- a/liblog/tests/Android.bp
+++ b/liblog/tests/Android.bp
@@ -54,6 +54,8 @@
],
srcs: [
"libc_test.cpp",
+ "liblog_default_tag.cpp",
+ "liblog_global_state.cpp",
"liblog_test.cpp",
"log_id_test.cpp",
"log_radio_test.cpp",
diff --git a/liblog/tests/liblog_default_tag.cpp b/liblog/tests/liblog_default_tag.cpp
new file mode 100644
index 0000000..a5baa9f
--- /dev/null
+++ b/liblog/tests/liblog_default_tag.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.
+ */
+
+// LOG_TAG must be unset for android-base's logging to use a default tag.
+#undef LOG_TAG
+
+#include <stdlib.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android/log.h>
+
+#include <gtest/gtest.h>
+
+TEST(liblog_default_tag, no_default_tag_libbase_write_first) {
+ using namespace android::base;
+ bool message_seen = false;
+ std::string expected_tag = "";
+ SetLogger([&](LogId, LogSeverity, const char* tag, const char*, unsigned int, const char*) {
+ message_seen = true;
+ EXPECT_EQ(expected_tag, tag);
+ });
+
+ expected_tag = getprogname();
+ LOG(WARNING) << "message";
+ EXPECT_TRUE(message_seen);
+ message_seen = false;
+
+ __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_WARN, nullptr, "message");
+ EXPECT_TRUE(message_seen);
+}
+
+TEST(liblog_default_tag, no_default_tag_liblog_write_first) {
+ using namespace android::base;
+ bool message_seen = false;
+ std::string expected_tag = "";
+ SetLogger([&](LogId, LogSeverity, const char* tag, const char*, unsigned int, const char*) {
+ message_seen = true;
+ EXPECT_EQ(expected_tag, tag);
+ });
+
+ expected_tag = getprogname();
+ __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_WARN, nullptr, "message");
+ EXPECT_TRUE(message_seen);
+ message_seen = false;
+
+ LOG(WARNING) << "message";
+ EXPECT_TRUE(message_seen);
+}
+
+TEST(liblog_default_tag, libbase_sets_default_tag) {
+ using namespace android::base;
+ bool message_seen = false;
+ std::string expected_tag = "libbase_test_tag";
+ SetLogger([&](LogId, LogSeverity, const char* tag, const char*, unsigned int, const char*) {
+ message_seen = true;
+ EXPECT_EQ(expected_tag, tag);
+ });
+ SetDefaultTag(expected_tag);
+
+ __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_WARN, nullptr, "message");
+ EXPECT_TRUE(message_seen);
+ message_seen = false;
+
+ LOG(WARNING) << "message";
+ EXPECT_TRUE(message_seen);
+}
+
+TEST(liblog_default_tag, liblog_sets_default_tag) {
+ using namespace android::base;
+ bool message_seen = false;
+ std::string expected_tag = "liblog_test_tag";
+ SetLogger([&](LogId, LogSeverity, const char* tag, const char*, unsigned int, const char*) {
+ message_seen = true;
+ EXPECT_EQ(expected_tag, tag);
+ });
+ __android_log_set_default_tag(expected_tag.c_str());
+
+ __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_WARN, nullptr, "message");
+ EXPECT_TRUE(message_seen);
+ message_seen = false;
+
+ LOG(WARNING) << "message";
+ EXPECT_TRUE(message_seen);
+}
\ No newline at end of file
diff --git a/liblog/tests/liblog_global_state.cpp b/liblog/tests/liblog_global_state.cpp
new file mode 100644
index 0000000..8d73bb8
--- /dev/null
+++ b/liblog/tests/liblog_global_state.cpp
@@ -0,0 +1,243 @@
+/*
+ * 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 LOG_TAG "global_state_test_tag"
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android/log.h>
+
+#include <gtest/gtest.h>
+
+TEST(liblog_global_state, libbase_logs_with_libbase_SetLogger) {
+ using namespace android::base;
+ bool message_seen = false;
+ LogSeverity expected_severity = WARNING;
+ std::string expected_file = Basename(__FILE__);
+ unsigned int expected_line;
+ std::string expected_message = "libbase test message";
+
+ auto LoggerFunction = [&](LogId log_id, LogSeverity severity, const char* tag, const char* file,
+ unsigned int line, const char* message) {
+ message_seen = true;
+ EXPECT_EQ(DEFAULT, log_id);
+ EXPECT_EQ(expected_severity, severity);
+ EXPECT_STREQ(LOG_TAG, tag);
+ EXPECT_EQ(expected_file, file);
+ EXPECT_EQ(expected_line, line);
+ EXPECT_EQ(expected_message, message);
+ };
+
+ SetLogger(LoggerFunction);
+
+ expected_line = __LINE__ + 1;
+ LOG(expected_severity) << expected_message;
+ EXPECT_TRUE(message_seen);
+}
+
+TEST(liblog_global_state, libbase_logs_with_liblog_set_logger) {
+ using namespace android::base;
+ // These must be static since they're used by the liblog logger function, which only accepts
+ // lambdas without captures. The items used by the libbase logger are explicitly not static, to
+ // ensure that lambdas with captures do work there.
+ static bool message_seen = false;
+ static std::string expected_file = Basename(__FILE__);
+ 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) {
+ 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);
+ };
+
+ __android_log_set_logger(liblog_logger_function);
+
+ expected_line = __LINE__ + 1;
+ LOG(WARNING) << expected_message;
+ EXPECT_TRUE(message_seen);
+}
+
+TEST(liblog_global_state, liblog_logs_with_libbase_SetLogger) {
+ using namespace android::base;
+ bool message_seen = false;
+ std::string expected_message = "libbase test message";
+
+ auto LoggerFunction = [&](LogId log_id, LogSeverity severity, const char* tag, const char* file,
+ unsigned int line, const char* message) {
+ message_seen = true;
+ EXPECT_EQ(MAIN, log_id);
+ EXPECT_EQ(WARNING, severity);
+ EXPECT_STREQ(LOG_TAG, tag);
+ EXPECT_EQ(nullptr, file);
+ EXPECT_EQ(0U, line);
+ EXPECT_EQ(expected_message, message);
+ };
+
+ SetLogger(LoggerFunction);
+
+ __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_WARN, LOG_TAG, expected_message.c_str());
+ EXPECT_TRUE(message_seen);
+ message_seen = false;
+}
+
+TEST(liblog_global_state, liblog_logs_with_liblog_set_logger) {
+ using namespace android::base;
+ // These must be static since they're used by the liblog logger function, which only accepts
+ // lambdas without captures. The items used by the libbase logger are explicitly not static, to
+ // ensure that lambdas with captures do work there.
+ static bool message_seen = false;
+ static int expected_buffer_id = LOG_ID_MAIN;
+ 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) {
+ 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);
+ };
+
+ __android_log_set_logger(liblog_logger_function);
+
+ __android_log_buf_write(expected_buffer_id, expected_priority, LOG_TAG, expected_message.c_str());
+ EXPECT_TRUE(message_seen);
+}
+
+TEST(liblog_global_state, SetAborter_with_liblog) {
+ using namespace android::base;
+
+ std::string expected_message = "libbase test message";
+ static bool message_seen = false;
+ auto aborter_function = [&](const char* message) {
+ message_seen = true;
+ EXPECT_EQ(expected_message, message);
+ };
+
+ SetAborter(aborter_function);
+ LOG(FATAL) << expected_message;
+ EXPECT_TRUE(message_seen);
+ message_seen = false;
+
+ static std::string expected_message_static = "libbase test message";
+ auto liblog_aborter_function = [](const char* message) {
+ message_seen = true;
+ EXPECT_EQ(expected_message_static, message);
+ };
+ __android_log_set_aborter(liblog_aborter_function);
+ LOG(FATAL) << expected_message_static;
+ EXPECT_TRUE(message_seen);
+ message_seen = false;
+}
+
+TEST(liblog_global_state, is_loggable_both_default) {
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+}
+
+TEST(liblog_global_state, is_loggable_minimum_log_priority_only) {
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+ EXPECT_EQ(ANDROID_LOG_DEFAULT, __android_log_set_minimum_priority(ANDROID_LOG_DEBUG));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+ EXPECT_EQ(ANDROID_LOG_DEBUG, __android_log_set_minimum_priority(ANDROID_LOG_WARN));
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+ EXPECT_EQ(android::base::WARNING, android::base::SetMinimumLogSeverity(android::base::DEBUG));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+ EXPECT_EQ(android::base::DEBUG, android::base::SetMinimumLogSeverity(android::base::WARNING));
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+}
+
+TEST(liblog_global_state, is_loggable_tag_log_priority_only) {
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+ auto log_tag_property = std::string("log.tag.") + LOG_TAG;
+ android::base::SetProperty(log_tag_property, "d");
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+ android::base::SetProperty(log_tag_property, "w");
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+ android::base::SetProperty(log_tag_property, "");
+}
+
+TEST(liblog_global_state, is_loggable_both_set) {
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+ // When both a tag and a minimum priority are set, we use the lower value of the two.
+
+ // tag = warning, minimum_priority = debug, expect 'debug'
+ auto log_tag_property = std::string("log.tag.") + LOG_TAG;
+ android::base::SetProperty(log_tag_property, "w");
+ EXPECT_EQ(ANDROID_LOG_DEFAULT, __android_log_set_minimum_priority(ANDROID_LOG_DEBUG));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+ // tag = warning, minimum_priority = warning, expect 'warning'
+ EXPECT_EQ(ANDROID_LOG_DEBUG, __android_log_set_minimum_priority(ANDROID_LOG_WARN));
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+ // tag = debug, minimum_priority = warning, expect 'debug'
+ android::base::SetProperty(log_tag_property, "d");
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+ // tag = debug, minimum_priority = debug, expect 'debug'
+ EXPECT_EQ(ANDROID_LOG_WARN, __android_log_set_minimum_priority(ANDROID_LOG_DEBUG));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+ android::base::SetProperty(log_tag_property, "");
+}
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index c402e20..75a26bf 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -1074,7 +1074,6 @@
// Once we've found our expected entry, break.
if (len == LOGGER_ENTRY_MAX_PAYLOAD - sizeof(big_payload_tag)) {
- EXPECT_EQ(ret, len + static_cast<ssize_t>(sizeof(big_payload_tag)));
*found = true;
}
};
@@ -1259,14 +1258,10 @@
int level;
char type;
} levels[] = {
- { ANDROID_LOG_VERBOSE, 'v' },
- { ANDROID_LOG_DEBUG, 'd' },
- { ANDROID_LOG_INFO, 'i' },
- { ANDROID_LOG_WARN, 'w' },
- { ANDROID_LOG_ERROR, 'e' },
- { ANDROID_LOG_FATAL, 'a' },
- { -1, 's' },
- { -2, 'g' }, // Illegal value, resort to default
+ {ANDROID_LOG_VERBOSE, 'v'}, {ANDROID_LOG_DEBUG, 'd'},
+ {ANDROID_LOG_INFO, 'i'}, {ANDROID_LOG_WARN, 'w'},
+ {ANDROID_LOG_ERROR, 'e'}, {ANDROID_LOG_FATAL, 'a'},
+ {ANDROID_LOG_SILENT, 's'}, {-2, 'g'}, // Illegal value, resort to default
};
// Set up initial test condition
diff --git a/libprocinfo/process.cpp b/libprocinfo/process.cpp
index 9194cf3..2efd49c 100644
--- a/libprocinfo/process.cpp
+++ b/libprocinfo/process.cpp
@@ -59,7 +59,6 @@
case 'Z':
return kProcessStateZombie;
default:
- LOG(ERROR) << "unknown process state: " << *state;
return kProcessStateUnknown;
}
}
diff --git a/libstats/socket/Android.bp b/libstats/socket/Android.bp
index 3b6efbb..9fd9fbc 100644
--- a/libstats/socket/Android.bp
+++ b/libstats/socket/Android.bp
@@ -75,3 +75,22 @@
"libgtest_prod",
],
}
+
+cc_test {
+ name: "libstatssocket_test",
+ srcs: ["tests/stats_event_test.cpp"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ static_libs: [
+ "libgmock",
+ "libstatssocket",
+ ],
+ shared_libs: [
+ "libcutils",
+ "liblog",
+ "libutils",
+ ],
+ test_suites: ["device_tests"],
+}
diff --git a/libstats/socket/tests/stats_event_test.cpp b/libstats/socket/tests/stats_event_test.cpp
new file mode 100644
index 0000000..cf0592c
--- /dev/null
+++ b/libstats/socket/tests/stats_event_test.cpp
@@ -0,0 +1,344 @@
+/*
+ * 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 "stats_event.h"
+#include <gtest/gtest.h>
+#include <utils/SystemClock.h>
+
+using std::string;
+using std::vector;
+
+// Side-effect: this function moves the start of the buffer past the read value
+template <class T>
+T readNext(uint8_t** buffer) {
+ T value = *(T*)(*buffer);
+ *buffer += sizeof(T);
+ return value;
+}
+
+void checkTypeHeader(uint8_t** buffer, uint8_t typeId, uint8_t numAnnotations = 0) {
+ uint8_t typeHeader = (numAnnotations << 4) | typeId;
+ EXPECT_EQ(readNext<uint8_t>(buffer), typeHeader);
+}
+
+template <class T>
+void checkScalar(uint8_t** buffer, T expectedValue) {
+ EXPECT_EQ(readNext<T>(buffer), expectedValue);
+}
+
+void checkString(uint8_t** buffer, const string& expectedString) {
+ uint32_t size = readNext<uint32_t>(buffer);
+ string parsedString((char*)(*buffer), size);
+ EXPECT_EQ(parsedString, expectedString);
+ *buffer += size; // move buffer past string we just read
+}
+
+void checkByteArray(uint8_t** buffer, const vector<uint8_t>& expectedByteArray) {
+ uint32_t size = readNext<uint32_t>(buffer);
+ vector<uint8_t> parsedByteArray(*buffer, *buffer + size);
+ EXPECT_EQ(parsedByteArray, expectedByteArray);
+ *buffer += size; // move buffer past byte array we just read
+}
+
+template <class T>
+void checkAnnotation(uint8_t** buffer, uint8_t annotationId, uint8_t typeId, T annotationValue) {
+ EXPECT_EQ(readNext<uint8_t>(buffer), annotationId);
+ EXPECT_EQ(readNext<uint8_t>(buffer), typeId);
+ checkScalar<T>(buffer, annotationValue);
+}
+
+void checkMetadata(uint8_t** buffer, uint8_t numElements, int64_t startTime, int64_t endTime,
+ uint32_t atomId) {
+ // All events start with OBJECT_TYPE id.
+ checkTypeHeader(buffer, OBJECT_TYPE);
+
+ // We increment by 2 because the number of elements listed in the
+ // serialization accounts for the timestamp and atom id as well.
+ checkScalar(buffer, static_cast<uint8_t>(numElements + 2));
+
+ // Check timestamp
+ checkTypeHeader(buffer, INT64_TYPE);
+ int64_t timestamp = readNext<int64_t>(buffer);
+ EXPECT_GE(timestamp, startTime);
+ EXPECT_LE(timestamp, endTime);
+
+ // Check atom id
+ checkTypeHeader(buffer, INT32_TYPE);
+ checkScalar(buffer, atomId);
+}
+
+TEST(StatsEventTest, TestScalars) {
+ uint32_t atomId = 100;
+ int32_t int32Value = -5;
+ int64_t int64Value = -2 * android::elapsedRealtimeNano();
+ float floatValue = 2.0;
+ bool boolValue = false;
+
+ int64_t startTime = android::elapsedRealtimeNano();
+ struct stats_event* event = stats_event_obtain();
+ stats_event_set_atom_id(event, atomId);
+ stats_event_write_int32(event, int32Value);
+ stats_event_write_int64(event, int64Value);
+ stats_event_write_float(event, floatValue);
+ stats_event_write_bool(event, boolValue);
+ stats_event_build(event);
+ int64_t endTime = android::elapsedRealtimeNano();
+
+ size_t bufferSize;
+ uint8_t* buffer = stats_event_get_buffer(event, &bufferSize);
+ uint8_t* bufferEnd = buffer + bufferSize;
+
+ checkMetadata(&buffer, /*numElements=*/4, startTime, endTime, atomId);
+
+ // check int32 element
+ checkTypeHeader(&buffer, INT32_TYPE);
+ checkScalar(&buffer, int32Value);
+
+ // check int64 element
+ checkTypeHeader(&buffer, INT64_TYPE);
+ checkScalar(&buffer, int64Value);
+
+ // check float element
+ checkTypeHeader(&buffer, FLOAT_TYPE);
+ checkScalar(&buffer, floatValue);
+
+ // check bool element
+ checkTypeHeader(&buffer, BOOL_TYPE);
+ checkScalar(&buffer, boolValue);
+
+ EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer
+ EXPECT_EQ(stats_event_get_errors(event), 0);
+ stats_event_release(event);
+}
+
+TEST(StatsEventTest, TestStrings) {
+ uint32_t atomId = 100;
+ string str = "test_string";
+
+ int64_t startTime = android::elapsedRealtimeNano();
+ struct stats_event* event = stats_event_obtain();
+ stats_event_set_atom_id(event, atomId);
+ stats_event_write_string8(event, str.c_str());
+ stats_event_build(event);
+ int64_t endTime = android::elapsedRealtimeNano();
+
+ size_t bufferSize;
+ uint8_t* buffer = stats_event_get_buffer(event, &bufferSize);
+ uint8_t* bufferEnd = buffer + bufferSize;
+
+ checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
+
+ checkTypeHeader(&buffer, STRING_TYPE);
+ checkString(&buffer, str);
+
+ EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer
+ EXPECT_EQ(stats_event_get_errors(event), 0);
+ stats_event_release(event);
+}
+
+TEST(StatsEventTest, TestByteArrays) {
+ uint32_t atomId = 100;
+ vector<uint8_t> message = {'b', 'y', 't', '\0', 'e', 's'};
+
+ int64_t startTime = android::elapsedRealtimeNano();
+ struct stats_event* event = stats_event_obtain();
+ stats_event_set_atom_id(event, atomId);
+ stats_event_write_byte_array(event, message.data(), message.size());
+ stats_event_build(event);
+ int64_t endTime = android::elapsedRealtimeNano();
+
+ size_t bufferSize;
+ uint8_t* buffer = stats_event_get_buffer(event, &bufferSize);
+ uint8_t* bufferEnd = buffer + bufferSize;
+
+ checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
+
+ checkTypeHeader(&buffer, BYTE_ARRAY_TYPE);
+ checkByteArray(&buffer, message);
+
+ EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer
+ EXPECT_EQ(stats_event_get_errors(event), 0);
+ stats_event_release(event);
+}
+
+TEST(StatsEventTest, TestAttributionChains) {
+ uint32_t atomId = 100;
+
+ uint8_t numNodes = 50;
+ uint32_t uids[numNodes];
+ vector<string> tags(numNodes); // storage that cTag elements point to
+ const char* cTags[numNodes];
+ for (int i = 0; i < (int)numNodes; i++) {
+ uids[i] = i;
+ tags.push_back("test" + std::to_string(i));
+ cTags[i] = tags[i].c_str();
+ }
+
+ int64_t startTime = android::elapsedRealtimeNano();
+ struct stats_event* event = stats_event_obtain();
+ stats_event_set_atom_id(event, atomId);
+ stats_event_write_attribution_chain(event, uids, cTags, numNodes);
+ stats_event_build(event);
+ int64_t endTime = android::elapsedRealtimeNano();
+
+ size_t bufferSize;
+ uint8_t* buffer = stats_event_get_buffer(event, &bufferSize);
+ uint8_t* bufferEnd = buffer + bufferSize;
+
+ checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
+
+ checkTypeHeader(&buffer, ATTRIBUTION_CHAIN_TYPE);
+ checkScalar(&buffer, numNodes);
+ for (int i = 0; i < numNodes; i++) {
+ checkScalar(&buffer, uids[i]);
+ checkString(&buffer, tags[i]);
+ }
+
+ EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer
+ EXPECT_EQ(stats_event_get_errors(event), 0);
+ stats_event_release(event);
+}
+
+TEST(StatsEventTest, TestKeyValuePairs) {
+ uint32_t atomId = 100;
+
+ uint8_t numPairs = 4;
+ struct key_value_pair pairs[numPairs];
+ pairs[0] = {.key = 0, .valueType = INT32_TYPE, .int32Value = -1};
+ pairs[1] = {.key = 1, .valueType = INT64_TYPE, .int64Value = 0x123456789};
+ pairs[2] = {.key = 2, .valueType = FLOAT_TYPE, .floatValue = 5.5};
+ string str = "test_key_value_pair_string";
+ pairs[3] = {.key = 3, .valueType = STRING_TYPE, .stringValue = str.c_str()};
+
+ int64_t startTime = android::elapsedRealtimeNano();
+ struct stats_event* event = stats_event_obtain();
+ stats_event_set_atom_id(event, atomId);
+ stats_event_write_key_value_pairs(event, pairs, numPairs);
+ stats_event_build(event);
+ int64_t endTime = android::elapsedRealtimeNano();
+
+ size_t bufferSize;
+ uint8_t* buffer = stats_event_get_buffer(event, &bufferSize);
+ uint8_t* bufferEnd = buffer + bufferSize;
+
+ checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
+
+ checkTypeHeader(&buffer, KEY_VALUE_PAIRS_TYPE);
+ checkScalar(&buffer, numPairs);
+
+ // first pair
+ checkScalar(&buffer, pairs[0].key);
+ checkTypeHeader(&buffer, pairs[0].valueType);
+ checkScalar(&buffer, pairs[0].int32Value);
+
+ // second pair
+ checkScalar(&buffer, pairs[1].key);
+ checkTypeHeader(&buffer, pairs[1].valueType);
+ checkScalar(&buffer, pairs[1].int64Value);
+
+ // third pair
+ checkScalar(&buffer, pairs[2].key);
+ checkTypeHeader(&buffer, pairs[2].valueType);
+ checkScalar(&buffer, pairs[2].floatValue);
+
+ // fourth pair
+ checkScalar(&buffer, pairs[3].key);
+ checkTypeHeader(&buffer, pairs[3].valueType);
+ checkString(&buffer, str);
+
+ EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer
+ EXPECT_EQ(stats_event_get_errors(event), 0);
+ stats_event_release(event);
+}
+
+TEST(StatsEventTest, TestAnnotations) {
+ uint32_t atomId = 100;
+
+ // first element information
+ bool boolValue = false;
+ uint8_t boolAnnotation1Id = 1;
+ uint8_t boolAnnotation2Id = 2;
+ bool boolAnnotation1Value = true;
+ int32_t boolAnnotation2Value = 3;
+
+ // second element information
+ float floatValue = -5.0;
+ uint8_t floatAnnotation1Id = 3;
+ uint8_t floatAnnotation2Id = 4;
+ int32_t floatAnnotation1Value = 8;
+ bool floatAnnotation2Value = false;
+
+ int64_t startTime = android::elapsedRealtimeNano();
+ struct stats_event* event = stats_event_obtain();
+ stats_event_set_atom_id(event, 100);
+ stats_event_write_bool(event, boolValue);
+ stats_event_add_bool_annotation(event, boolAnnotation1Id, boolAnnotation1Value);
+ stats_event_add_int32_annotation(event, boolAnnotation2Id, boolAnnotation2Value);
+ stats_event_write_float(event, floatValue);
+ stats_event_add_int32_annotation(event, floatAnnotation1Id, floatAnnotation1Value);
+ stats_event_add_bool_annotation(event, floatAnnotation2Id, floatAnnotation2Value);
+ stats_event_build(event);
+ int64_t endTime = android::elapsedRealtimeNano();
+
+ size_t bufferSize;
+ uint8_t* buffer = stats_event_get_buffer(event, &bufferSize);
+ uint8_t* bufferEnd = buffer + bufferSize;
+
+ checkMetadata(&buffer, /*numElements=*/2, startTime, endTime, atomId);
+
+ // check first element
+ checkTypeHeader(&buffer, BOOL_TYPE, /*numAnnotations=*/2);
+ checkScalar(&buffer, boolValue);
+ checkAnnotation(&buffer, boolAnnotation1Id, BOOL_TYPE, boolAnnotation1Value);
+ checkAnnotation(&buffer, boolAnnotation2Id, INT32_TYPE, boolAnnotation2Value);
+
+ // check second element
+ checkTypeHeader(&buffer, FLOAT_TYPE, /*numAnnotations=*/2);
+ checkScalar(&buffer, floatValue);
+ checkAnnotation(&buffer, floatAnnotation1Id, INT32_TYPE, floatAnnotation1Value);
+ checkAnnotation(&buffer, floatAnnotation2Id, BOOL_TYPE, floatAnnotation2Value);
+
+ EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer
+ EXPECT_EQ(stats_event_get_errors(event), 0);
+ stats_event_release(event);
+}
+
+TEST(StatsEventTest, TestNoAtomIdError) {
+ struct stats_event* event = stats_event_obtain();
+ // Don't set the atom id in order to trigger the error.
+ stats_event_build(event);
+
+ uint32_t errors = stats_event_get_errors(event);
+ EXPECT_NE(errors | ERROR_NO_ATOM_ID, 0);
+
+ stats_event_release(event);
+}
+
+TEST(StatsEventTest, TestOverflowError) {
+ struct stats_event* event = stats_event_obtain();
+ stats_event_set_atom_id(event, 100);
+ // Add 1000 int32s to the event. Each int32 takes 5 bytes so this will
+ // overflow the 4068 byte buffer.
+ for (int i = 0; i < 1000; i++) {
+ stats_event_write_int32(event, 0);
+ }
+ stats_event_build(event);
+
+ uint32_t errors = stats_event_get_errors(event);
+ EXPECT_NE(errors | ERROR_OVERFLOW, 0);
+
+ stats_event_release(event);
+}
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 9cd3d65..3695f72 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -240,6 +240,7 @@
"tests/files/offline/debug_frame_load_bias_arm/*",
"tests/files/offline/eh_frame_bias_x86/*",
"tests/files/offline/eh_frame_hdr_begin_x86_64/*",
+ "tests/files/offline/empty_arm64/*",
"tests/files/offline/invalid_elf_offset_arm/*",
"tests/files/offline/jit_debug_arm/*",
"tests/files/offline/jit_debug_x86/*",
diff --git a/libunwindstack/Global.cpp b/libunwindstack/Global.cpp
index ec977e1..ee6c8a5 100644
--- a/libunwindstack/Global.cpp
+++ b/libunwindstack/Global.cpp
@@ -70,30 +70,28 @@
// This also works:
// f0000-f2000 0 r-- /system/lib/libc.so
// f2000-f3000 2000 rw- /system/lib/libc.so
- MapInfo* map_start = nullptr;
+ // It is also possible to see empty maps after the read-only like so:
+ // f0000-f1000 0 r-- /system/lib/libc.so
+ // f1000-f2000 0 ---
+ // f2000-f3000 1000 r-x /system/lib/libc.so
+ // f3000-f4000 2000 rw- /system/lib/libc.so
+ MapInfo* map_zero = nullptr;
for (const auto& info : *maps) {
- if (map_start != nullptr && map_start->name == info->name) {
- if (info->offset != 0 &&
- (info->flags & (PROT_READ | PROT_WRITE)) == (PROT_READ | PROT_WRITE)) {
- Elf* elf = map_start->GetElf(memory_, arch());
- uint64_t ptr;
- if (elf->GetGlobalVariableOffset(variable, &ptr) && ptr != 0) {
- uint64_t offset_end = info->offset + info->end - info->start;
- if (ptr >= info->offset && ptr < offset_end) {
- ptr = info->start + ptr - info->offset;
- if (ReadVariableData(ptr)) {
- break;
- }
+ if (info->offset != 0 && (info->flags & (PROT_READ | PROT_WRITE)) == (PROT_READ | PROT_WRITE) &&
+ map_zero != nullptr && Searchable(info->name) && info->name == map_zero->name) {
+ Elf* elf = map_zero->GetElf(memory_, arch());
+ uint64_t ptr;
+ if (elf->GetGlobalVariableOffset(variable, &ptr) && ptr != 0) {
+ uint64_t offset_end = info->offset + info->end - info->start;
+ if (ptr >= info->offset && ptr < offset_end) {
+ ptr = info->start + ptr - info->offset;
+ if (ReadVariableData(ptr)) {
+ break;
}
}
- map_start = nullptr;
}
- } else {
- map_start = nullptr;
- }
- if (map_start == nullptr && (info->flags & PROT_READ) && info->offset == 0 &&
- Searchable(info->name)) {
- map_start = info.get();
+ } else if (info->offset == 0 && !info->name.empty()) {
+ map_zero = info.get();
}
}
}
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index f2dad84..31f3144 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -37,12 +37,12 @@
bool MapInfo::InitFileMemoryFromPreviousReadOnlyMap(MemoryFileAtOffset* memory) {
// One last attempt, see if the previous map is read-only with the
// same name and stretches across this map.
- if (prev_map == nullptr || prev_map->flags != PROT_READ) {
+ if (prev_real_map == nullptr || prev_real_map->flags != PROT_READ) {
return false;
}
- uint64_t map_size = end - prev_map->end;
- if (!memory->Init(name, prev_map->offset, map_size)) {
+ uint64_t map_size = end - prev_real_map->end;
+ if (!memory->Init(name, prev_real_map->offset, map_size)) {
return false;
}
@@ -51,12 +51,12 @@
return false;
}
- if (!memory->Init(name, prev_map->offset, max_size)) {
+ if (!memory->Init(name, prev_real_map->offset, max_size)) {
return false;
}
- elf_offset = offset - prev_map->offset;
- elf_start_offset = prev_map->offset;
+ elf_offset = offset - prev_real_map->offset;
+ elf_start_offset = prev_real_map->offset;
return true;
}
@@ -112,8 +112,8 @@
// Need to check how to set the elf start offset. If this map is not
// the r-x map of a r-- map, then use the real offset value. Otherwise,
// use 0.
- if (prev_map == nullptr || prev_map->offset != 0 || prev_map->flags != PROT_READ ||
- prev_map->name != name) {
+ if (prev_real_map == nullptr || prev_real_map->offset != 0 ||
+ prev_real_map->flags != PROT_READ || prev_real_map->name != name) {
elf_start_offset = offset;
}
return memory.release();
@@ -172,20 +172,20 @@
// doesn't guarantee that this invariant will always be true. However,
// if that changes, there is likely something else that will change and
// break something.
- if (offset == 0 || name.empty() || prev_map == nullptr || prev_map->name != name ||
- prev_map->offset >= offset) {
+ if (offset == 0 || name.empty() || prev_real_map == nullptr || prev_real_map->name != name ||
+ prev_real_map->offset >= offset) {
return nullptr;
}
// Make sure that relative pc values are corrected properly.
- elf_offset = offset - prev_map->offset;
+ elf_offset = offset - prev_real_map->offset;
// Use this as the elf start offset, otherwise, you always get offsets into
// the r-x section, which is not quite the right information.
- elf_start_offset = prev_map->offset;
+ elf_start_offset = prev_real_map->offset;
MemoryRanges* ranges = new MemoryRanges;
- ranges->Insert(
- new MemoryRange(process_memory, prev_map->start, prev_map->end - prev_map->start, 0));
+ ranges->Insert(new MemoryRange(process_memory, prev_real_map->start,
+ prev_real_map->end - prev_real_map->start, 0));
ranges->Insert(new MemoryRange(process_memory, start, end - start, elf_offset));
memory_backed_elf = true;
@@ -236,15 +236,15 @@
if (!elf->valid()) {
elf_start_offset = offset;
- } else if (prev_map != nullptr && elf_start_offset != offset &&
- prev_map->offset == elf_start_offset && prev_map->name == name) {
+ } else if (prev_real_map != nullptr && elf_start_offset != offset &&
+ prev_real_map->offset == elf_start_offset && prev_real_map->name == name) {
// If there is a read-only map then a read-execute map that represents the
// same elf object, make sure the previous map is using the same elf
// object if it hasn't already been set.
- std::lock_guard<std::mutex> guard(prev_map->mutex_);
- if (prev_map->elf.get() == nullptr) {
- prev_map->elf = elf;
- prev_map->memory_backed_elf = memory_backed_elf;
+ std::lock_guard<std::mutex> guard(prev_real_map->mutex_);
+ if (prev_real_map->elf.get() == nullptr) {
+ prev_real_map->elf = elf;
+ prev_real_map->memory_backed_elf = memory_backed_elf;
}
}
return elf.get();
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index 0ab68db..8f49ad9 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -60,6 +60,8 @@
}
bool Maps::Parse() {
+ MapInfo* prev_map = nullptr;
+ MapInfo* prev_real_map = nullptr;
return android::procinfo::ReadMapFile(
GetMapsFile(),
[&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t, const char* name) {
@@ -67,17 +69,24 @@
if (strncmp(name, "/dev/", 5) == 0 && strncmp(name + 5, "ashmem/", 7) != 0) {
flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
}
- maps_.emplace_back(
- new MapInfo(maps_.empty() ? nullptr : maps_.back().get(), start, end, pgoff,
- flags, name));
+ maps_.emplace_back(new MapInfo(prev_map, prev_real_map, start, end, pgoff, flags, name));
+ prev_map = maps_.back().get();
+ if (!prev_map->IsBlank()) {
+ prev_real_map = prev_map;
+ }
});
}
void Maps::Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
const std::string& name, uint64_t load_bias) {
+ MapInfo* prev_map = maps_.empty() ? nullptr : maps_.back().get();
+ MapInfo* prev_real_map = prev_map;
+ while (prev_real_map != nullptr && prev_real_map->IsBlank()) {
+ prev_real_map = prev_real_map->prev_map;
+ }
+
auto map_info =
- std::make_unique<MapInfo>(maps_.empty() ? nullptr : maps_.back().get(), start, end, offset,
- flags, name);
+ std::make_unique<MapInfo>(prev_map, prev_real_map, start, end, offset, flags, name);
map_info->load_bias = load_bias;
maps_.emplace_back(std::move(map_info));
}
@@ -89,14 +98,21 @@
// Set the prev_map values on the info objects.
MapInfo* prev_map = nullptr;
+ MapInfo* prev_real_map = nullptr;
for (const auto& map_info : maps_) {
map_info->prev_map = prev_map;
+ map_info->prev_real_map = prev_real_map;
prev_map = map_info.get();
+ if (!prev_map->IsBlank()) {
+ prev_real_map = prev_map;
+ }
}
}
bool BufferMaps::Parse() {
std::string content(buffer_);
+ MapInfo* prev_map = nullptr;
+ MapInfo* prev_real_map = nullptr;
return android::procinfo::ReadMapFileContent(
&content[0],
[&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t, const char* name) {
@@ -104,9 +120,11 @@
if (strncmp(name, "/dev/", 5) == 0 && strncmp(name + 5, "ashmem/", 7) != 0) {
flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
}
- maps_.emplace_back(
- new MapInfo(maps_.empty() ? nullptr : maps_.back().get(), start, end, pgoff,
- flags, name));
+ maps_.emplace_back(new MapInfo(prev_map, prev_real_map, start, end, pgoff, flags, name));
+ prev_map = maps_.back().get();
+ if (!prev_map->IsBlank()) {
+ prev_real_map = prev_map;
+ }
});
}
diff --git a/libunwindstack/include/unwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h
index 8f0c516..052e79f 100644
--- a/libunwindstack/include/unwindstack/MapInfo.h
+++ b/libunwindstack/include/unwindstack/MapInfo.h
@@ -31,24 +31,26 @@
class MemoryFileAtOffset;
struct MapInfo {
- MapInfo(MapInfo* map_info, uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
- const char* name)
+ MapInfo(MapInfo* prev_map, MapInfo* prev_real_map, uint64_t start, uint64_t end, uint64_t offset,
+ uint64_t flags, const char* name)
: start(start),
end(end),
offset(offset),
flags(flags),
name(name),
- prev_map(map_info),
+ prev_map(prev_map),
+ prev_real_map(prev_real_map),
load_bias(INT64_MAX),
build_id(0) {}
- MapInfo(MapInfo* map_info, uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
- const std::string& name)
+ MapInfo(MapInfo* prev_map, MapInfo* prev_real_map, uint64_t start, uint64_t end, uint64_t offset,
+ uint64_t flags, const std::string& name)
: start(start),
end(end),
offset(offset),
flags(flags),
name(name),
- prev_map(map_info),
+ prev_map(prev_map),
+ prev_real_map(prev_real_map),
load_bias(INT64_MAX),
build_id(0) {}
~MapInfo();
@@ -71,6 +73,14 @@
uint64_t elf_start_offset = 0;
MapInfo* prev_map = nullptr;
+ // This is the previous map that is not empty with a 0 offset. For
+ // example, this set of maps:
+ // 1000-2000 r--p 000000 00:00 0 libc.so
+ // 2000-3000 ---p 000000 00:00 0 libc.so
+ // 3000-4000 r-xp 003000 00:00 0 libc.so
+ // The last map's prev_map would point to the 2000-3000 map, while the
+ // prev_real_map would point to the 1000-2000 map.
+ MapInfo* prev_real_map = nullptr;
std::atomic_int64_t load_bias;
@@ -97,6 +107,8 @@
// Returns the printable version of the build id (hex dump of raw data).
std::string GetPrintableBuildID();
+ inline bool IsBlank() { return offset == 0 && flags == 0 && name.empty(); }
+
private:
MapInfo(const MapInfo&) = delete;
void operator=(const MapInfo&) = delete;
diff --git a/libunwindstack/tests/DexFileTest.cpp b/libunwindstack/tests/DexFileTest.cpp
index 0149a42..1b54da6 100644
--- a/libunwindstack/tests/DexFileTest.cpp
+++ b/libunwindstack/tests/DexFileTest.cpp
@@ -105,7 +105,7 @@
static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
MemoryFake memory;
- MapInfo info(nullptr, 0, 0x10000, 0, 0x5, tf.path);
+ MapInfo info(nullptr, nullptr, 0, 0x10000, 0, 0x5, tf.path);
EXPECT_TRUE(DexFile::Create(0x500, &memory, &info) != nullptr);
}
@@ -118,7 +118,7 @@
static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
MemoryFake memory;
- MapInfo info(nullptr, 0x100, 0x10000, 0, 0x5, tf.path);
+ MapInfo info(nullptr, nullptr, 0x100, 0x10000, 0, 0x5, tf.path);
EXPECT_TRUE(DexFile::Create(0x600, &memory, &info) != nullptr);
}
@@ -131,21 +131,21 @@
static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
MemoryFake memory;
- MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, tf.path);
+ MapInfo info(nullptr, nullptr, 0x100, 0x10000, 0x200, 0x5, tf.path);
EXPECT_TRUE(DexFile::Create(0x400, &memory, &info) != nullptr);
}
TEST(DexFileTest, create_using_memory_empty_file) {
MemoryFake memory;
memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
- MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, "");
+ MapInfo info(nullptr, nullptr, 0x100, 0x10000, 0x200, 0x5, "");
EXPECT_TRUE(DexFile::Create(0x4000, &memory, &info) != nullptr);
}
TEST(DexFileTest, create_using_memory_file_does_not_exist) {
MemoryFake memory;
memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
- MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, "/does/not/exist");
+ MapInfo info(nullptr, nullptr, 0x100, 0x10000, 0x200, 0x5, "/does/not/exist");
EXPECT_TRUE(DexFile::Create(0x4000, &memory, &info) != nullptr);
}
@@ -158,7 +158,7 @@
MemoryFake memory;
memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
- MapInfo info(nullptr, 0x4000, 0x10000, 0x200, 0x5, "/does/not/exist");
+ MapInfo info(nullptr, nullptr, 0x4000, 0x10000, 0x200, 0x5, "/does/not/exist");
std::unique_ptr<DexFile> dex_file = DexFile::Create(0x4000, &memory, &info);
ASSERT_TRUE(dex_file != nullptr);
@@ -171,7 +171,7 @@
TEST(DexFileTest, get_method) {
MemoryFake memory;
memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
- MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, "");
+ MapInfo info(nullptr, nullptr, 0x100, 0x10000, 0x200, 0x5, "");
std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
ASSERT_TRUE(dex_file != nullptr);
@@ -189,7 +189,7 @@
TEST(DexFileTest, get_method_empty) {
MemoryFake memory;
memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
- MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, "");
+ MapInfo info(nullptr, nullptr, 0x100, 0x10000, 0x200, 0x5, "");
std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
ASSERT_TRUE(dex_file != nullptr);
diff --git a/libunwindstack/tests/DexFilesTest.cpp b/libunwindstack/tests/DexFilesTest.cpp
index 0dd3af6..477cf8e 100644
--- a/libunwindstack/tests/DexFilesTest.cpp
+++ b/libunwindstack/tests/DexFilesTest.cpp
@@ -64,7 +64,11 @@
"f000-11000 r--p 00000000 00:00 0 /fake/elf3\n"
"100000-110000 rw-p 00f1000 00:00 0 /fake/elf3\n"
"200000-210000 rw-p 0002000 00:00 0 /fake/elf3\n"
- "300000-400000 rw-p 0003000 00:00 0 /fake/elf3\n"));
+ "300000-400000 rw-p 0003000 00:00 0 /fake/elf3\n"
+ "500000-501000 r--p 0000000 00:00 0 /fake/elf4\n"
+ "501000-502000 ---p 0000000 00:00 0\n"
+ "503000-510000 rw-p 0003000 00:00 0 /fake/elf4\n"
+ "510000-520000 rw-p 0010000 00:00 0 /fake/elf4\n"));
ASSERT_TRUE(maps_->Parse());
// Global variable in a section that is not readable.
@@ -81,6 +85,11 @@
map_info = maps_->Get(kMapGlobal);
ASSERT_TRUE(map_info != nullptr);
CreateFakeElf(map_info, 0xf1800, 0xf1000, 0xf1000, 0x10000);
+
+ // Global variable set in this map, but there is an empty map before rw map.
+ map_info = maps_->Get(kMapGlobalAfterEmpty);
+ ASSERT_TRUE(map_info != nullptr);
+ CreateFakeElf(map_info, 0x3800, 0x3000, 0x3000, 0xd000);
}
void SetUp() override {
@@ -102,6 +111,8 @@
static constexpr size_t kMapGlobalRw = 6;
static constexpr size_t kMapDexFileEntries = 7;
static constexpr size_t kMapDexFiles = 8;
+ static constexpr size_t kMapGlobalAfterEmpty = 9;
+ static constexpr size_t kMapDexFilesAfterEmpty = 12;
std::shared_ptr<Memory> process_memory_;
MemoryFake* memory_;
@@ -328,4 +339,18 @@
EXPECT_EQ(0x123U, method_offset);
}
+TEST_F(DexFilesTest, get_method_information_with_empty_map) {
+ std::string method_name = "nothing";
+ uint64_t method_offset = 0x124;
+ MapInfo* info = maps_->Get(kMapDexFilesAfterEmpty);
+
+ WriteDescriptor32(0x503800, 0x506000);
+ WriteEntry32(0x506000, 0, 0, 0x510000);
+ WriteDex(0x510000);
+
+ dex_files_->GetMethodInformation(maps_.get(), info, 0x510100, &method_name, &method_offset);
+ EXPECT_EQ("Main.<init>", method_name);
+ EXPECT_EQ(0U, method_offset);
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/ElfCacheTest.cpp b/libunwindstack/tests/ElfCacheTest.cpp
index 5735858..5f13546 100644
--- a/libunwindstack/tests/ElfCacheTest.cpp
+++ b/libunwindstack/tests/ElfCacheTest.cpp
@@ -78,8 +78,8 @@
uint64_t start = 0x1000;
uint64_t end = 0x20000;
- MapInfo info1(nullptr, start, end, 0, 0x5, tf.path);
- MapInfo info2(nullptr, start, end, 0, 0x5, tf.path);
+ MapInfo info1(nullptr, nullptr, start, end, 0, 0x5, tf.path);
+ MapInfo info2(nullptr, nullptr, start, end, 0, 0x5, tf.path);
Elf* elf1 = info1.GetElf(memory_, ARCH_ARM);
ASSERT_TRUE(elf1->valid());
@@ -119,17 +119,17 @@
uint64_t start = 0x1000;
uint64_t end = 0x20000;
// Will have an elf at offset 0 in file.
- MapInfo info0_1(nullptr, start, end, 0, 0x5, tf.path);
- MapInfo info0_2(nullptr, start, end, 0, 0x5, tf.path);
+ MapInfo info0_1(nullptr, nullptr, start, end, 0, 0x5, tf.path);
+ MapInfo info0_2(nullptr, nullptr, start, end, 0, 0x5, tf.path);
// Will have an elf at offset 0x100 in file.
- MapInfo info100_1(nullptr, start, end, 0x100, 0x5, tf.path);
- MapInfo info100_2(nullptr, start, end, 0x100, 0x5, tf.path);
+ MapInfo info100_1(nullptr, nullptr, start, end, 0x100, 0x5, tf.path);
+ MapInfo info100_2(nullptr, nullptr, start, end, 0x100, 0x5, tf.path);
// Will have an elf at offset 0x200 in file.
- MapInfo info200_1(nullptr, start, end, 0x200, 0x5, tf.path);
- MapInfo info200_2(nullptr, start, end, 0x200, 0x5, tf.path);
+ MapInfo info200_1(nullptr, nullptr, start, end, 0x200, 0x5, tf.path);
+ MapInfo info200_2(nullptr, nullptr, start, end, 0x200, 0x5, tf.path);
// Will have an elf at offset 0 in file.
- MapInfo info300_1(nullptr, start, end, 0x300, 0x5, tf.path);
- MapInfo info300_2(nullptr, start, end, 0x300, 0x5, tf.path);
+ MapInfo info300_1(nullptr, nullptr, start, end, 0x300, 0x5, tf.path);
+ MapInfo info300_2(nullptr, nullptr, start, end, 0x300, 0x5, tf.path);
Elf* elf0_1 = info0_1.GetElf(memory_, ARCH_ARM);
ASSERT_TRUE(elf0_1->valid());
@@ -216,10 +216,10 @@
uint64_t start = 0x1000;
uint64_t end = 0x20000;
// Multiple info sections at different offsets will have non-zero elf offsets.
- MapInfo info300_1(nullptr, start, end, 0x300, 0x5, tf.path);
- MapInfo info300_2(nullptr, start, end, 0x300, 0x5, tf.path);
- MapInfo info400_1(nullptr, start, end, 0x400, 0x5, tf.path);
- MapInfo info400_2(nullptr, start, end, 0x400, 0x5, tf.path);
+ MapInfo info300_1(nullptr, nullptr, start, end, 0x300, 0x5, tf.path);
+ MapInfo info300_2(nullptr, nullptr, start, end, 0x300, 0x5, tf.path);
+ MapInfo info400_1(nullptr, nullptr, start, end, 0x400, 0x5, tf.path);
+ MapInfo info400_2(nullptr, nullptr, start, end, 0x400, 0x5, tf.path);
Elf* elf300_1 = info300_1.GetElf(memory_, ARCH_ARM);
ASSERT_TRUE(elf300_1->valid());
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index 4866345..1f3ed81 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -275,7 +275,7 @@
elf.FakeSetInterface(interface);
elf.FakeSetValid(true);
- MapInfo map_info(nullptr, 0x1000, 0x2000, 0, 0, "");
+ MapInfo map_info(nullptr, nullptr, 0x1000, 0x2000, 0, 0, "");
ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info));
diff --git a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
index 6c1cfa2..6d8d58e 100644
--- a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
+++ b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
@@ -89,7 +89,7 @@
};
TEST_F(MapInfoCreateMemoryTest, end_le_start) {
- MapInfo info(nullptr, 0x100, 0x100, 0, 0, elf_.path);
+ MapInfo info(nullptr, nullptr, 0x100, 0x100, 0, 0, elf_.path);
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() == nullptr);
@@ -108,7 +108,7 @@
// Verify that if the offset is non-zero but there is no elf at the offset,
// that the full file is used.
TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_full_file) {
- MapInfo info(nullptr, 0x100, 0x200, 0x100, 0, elf_.path);
+ MapInfo info(nullptr, nullptr, 0x100, 0x200, 0x100, 0, elf_.path);
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
@@ -129,8 +129,9 @@
// Now verify the elf start offset is set correctly based on the previous
// info.
- MapInfo prev_info(nullptr, 0, 0x100, 0x10, 0, "");
+ MapInfo prev_info(nullptr, nullptr, 0, 0x100, 0x10, 0, "");
info.prev_map = &prev_info;
+ info.prev_real_map = &prev_info;
// No preconditions met, change each one until it should set the elf start
// offset to zero.
@@ -177,7 +178,7 @@
// Verify that if the offset is non-zero and there is an elf at that
// offset, that only part of the file is used.
TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file) {
- MapInfo info(nullptr, 0x100, 0x200, 0x1000, 0, elf_at_1000_.path);
+ MapInfo info(nullptr, nullptr, 0x100, 0x200, 0x1000, 0, elf_at_1000_.path);
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
@@ -202,7 +203,7 @@
// embedded elf is bigger than the initial map, the new object is larger
// than the original map size. Do this for a 32 bit elf and a 64 bit elf.
TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_elf32) {
- MapInfo info(nullptr, 0x5000, 0x6000, 0x1000, 0, elf32_at_map_.path);
+ MapInfo info(nullptr, nullptr, 0x5000, 0x6000, 0x1000, 0, elf32_at_map_.path);
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
@@ -220,7 +221,7 @@
}
TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_elf64) {
- MapInfo info(nullptr, 0x7000, 0x8000, 0x2000, 0, elf64_at_map_.path);
+ MapInfo info(nullptr, nullptr, 0x7000, 0x8000, 0x2000, 0, elf64_at_map_.path);
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
@@ -243,14 +244,14 @@
// be returned if the file mapping fails, but the device check is incorrect.
std::vector<uint8_t> buffer(1024);
uint64_t start = reinterpret_cast<uint64_t>(buffer.data());
- MapInfo info(nullptr, start, start + buffer.size(), 0, 0x8000, "/dev/something");
+ MapInfo info(nullptr, nullptr, start, start + buffer.size(), 0, 0x8000, "/dev/something");
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() == nullptr);
}
TEST_F(MapInfoCreateMemoryTest, process_memory) {
- MapInfo info(nullptr, 0x2000, 0x3000, 0, PROT_READ, "");
+ MapInfo info(nullptr, nullptr, 0x2000, 0x3000, 0, PROT_READ, "");
Elf32_Ehdr ehdr = {};
TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
diff --git a/libunwindstack/tests/MapInfoGetBuildIDTest.cpp b/libunwindstack/tests/MapInfoGetBuildIDTest.cpp
index 16451d1..6953e26 100644
--- a/libunwindstack/tests/MapInfoGetBuildIDTest.cpp
+++ b/libunwindstack/tests/MapInfoGetBuildIDTest.cpp
@@ -50,7 +50,8 @@
elf_interface_ = new ElfInterfaceFake(memory_);
elf_->FakeSetInterface(elf_interface_);
elf_container_.reset(elf_);
- map_info_.reset(new MapInfo(nullptr, 0x1000, 0x20000, 0, PROT_READ | PROT_WRITE, tf_->path));
+ map_info_.reset(
+ new MapInfo(nullptr, nullptr, 0x1000, 0x20000, 0, PROT_READ | PROT_WRITE, tf_->path));
}
void MultipleThreadTest(std::string expected_build_id);
@@ -64,7 +65,7 @@
};
TEST_F(MapInfoGetBuildIDTest, no_elf_and_no_valid_elf_in_memory) {
- MapInfo info(nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
+ MapInfo info(nullptr, nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
EXPECT_EQ("", info.GetBuildID());
EXPECT_EQ("", info.GetPrintableBuildID());
diff --git a/libunwindstack/tests/MapInfoGetElfTest.cpp b/libunwindstack/tests/MapInfoGetElfTest.cpp
index d60b8b1..7f97814 100644
--- a/libunwindstack/tests/MapInfoGetElfTest.cpp
+++ b/libunwindstack/tests/MapInfoGetElfTest.cpp
@@ -68,7 +68,7 @@
};
TEST_F(MapInfoGetElfTest, invalid) {
- MapInfo info(nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
+ MapInfo info(nullptr, nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
// The map is empty, but this should still create an invalid elf object.
Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
@@ -77,7 +77,7 @@
}
TEST_F(MapInfoGetElfTest, valid32) {
- MapInfo info(nullptr, 0x3000, 0x4000, 0, PROT_READ, "");
+ MapInfo info(nullptr, nullptr, 0x3000, 0x4000, 0, PROT_READ, "");
Elf32_Ehdr ehdr;
TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
@@ -97,7 +97,7 @@
}
TEST_F(MapInfoGetElfTest, valid64) {
- MapInfo info(nullptr, 0x8000, 0x9000, 0, PROT_READ, "");
+ MapInfo info(nullptr, nullptr, 0x8000, 0x9000, 0, PROT_READ, "");
Elf64_Ehdr ehdr;
TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64);
@@ -111,7 +111,7 @@
}
TEST_F(MapInfoGetElfTest, invalid_arch_mismatch) {
- MapInfo info(nullptr, 0x3000, 0x4000, 0, PROT_READ, "");
+ MapInfo info(nullptr, nullptr, 0x3000, 0x4000, 0, PROT_READ, "");
Elf32_Ehdr ehdr;
TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
@@ -123,7 +123,7 @@
}
TEST_F(MapInfoGetElfTest, gnu_debugdata_init32) {
- MapInfo info(nullptr, 0x2000, 0x3000, 0, PROT_READ, "");
+ MapInfo info(nullptr, nullptr, 0x2000, 0x3000, 0, PROT_READ, "");
TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, true,
[&](uint64_t offset, const void* ptr, size_t size) {
@@ -139,7 +139,7 @@
}
TEST_F(MapInfoGetElfTest, gnu_debugdata_init64) {
- MapInfo info(nullptr, 0x5000, 0x8000, 0, PROT_READ, "");
+ MapInfo info(nullptr, nullptr, 0x5000, 0x8000, 0, PROT_READ, "");
TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, true,
[&](uint64_t offset, const void* ptr, size_t size) {
@@ -155,7 +155,7 @@
}
TEST_F(MapInfoGetElfTest, end_le_start) {
- MapInfo info(nullptr, 0x1000, 0x1000, 0, PROT_READ, elf_.path);
+ MapInfo info(nullptr, nullptr, 0x1000, 0x1000, 0, PROT_READ, elf_.path);
Elf32_Ehdr ehdr;
TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
@@ -182,7 +182,7 @@
// Verify that if the offset is non-zero but there is no elf at the offset,
// that the full file is used.
TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_full_file) {
- MapInfo info(nullptr, 0x1000, 0x2000, 0x100, PROT_READ, elf_.path);
+ MapInfo info(nullptr, nullptr, 0x1000, 0x2000, 0x100, PROT_READ, elf_.path);
std::vector<uint8_t> buffer(0x1000);
memset(buffer.data(), 0, buffer.size());
@@ -211,7 +211,7 @@
// Verify that if the offset is non-zero and there is an elf at that
// offset, that only part of the file is used.
TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file) {
- MapInfo info(nullptr, 0x1000, 0x2000, 0x2000, PROT_READ, elf_.path);
+ MapInfo info(nullptr, nullptr, 0x1000, 0x2000, 0x2000, PROT_READ, elf_.path);
std::vector<uint8_t> buffer(0x4000);
memset(buffer.data(), 0, buffer.size());
@@ -241,7 +241,7 @@
// embedded elf is bigger than the initial map, the new object is larger
// than the original map size. Do this for a 32 bit elf and a 64 bit elf.
TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf32) {
- MapInfo info(nullptr, 0x5000, 0x6000, 0x1000, PROT_READ, elf_.path);
+ MapInfo info(nullptr, nullptr, 0x5000, 0x6000, 0x1000, PROT_READ, elf_.path);
std::vector<uint8_t> buffer(0x4000);
memset(buffer.data(), 0, buffer.size());
@@ -269,7 +269,7 @@
}
TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf64) {
- MapInfo info(nullptr, 0x7000, 0x8000, 0x1000, PROT_READ, elf_.path);
+ MapInfo info(nullptr, nullptr, 0x7000, 0x8000, 0x1000, PROT_READ, elf_.path);
std::vector<uint8_t> buffer(0x4000);
memset(buffer.data(), 0, buffer.size());
@@ -297,7 +297,7 @@
}
TEST_F(MapInfoGetElfTest, check_device_maps) {
- MapInfo info(nullptr, 0x7000, 0x8000, 0x1000, PROT_READ | MAPS_FLAGS_DEVICE_MAP,
+ MapInfo info(nullptr, nullptr, 0x7000, 0x8000, 0x1000, PROT_READ | MAPS_FLAGS_DEVICE_MAP,
"/dev/something");
// Create valid elf data in process memory for this to verify that only
@@ -343,7 +343,7 @@
wait = true;
// Create all of the threads and have them do the GetElf at the same time
// to make it likely that a race will occur.
- MapInfo info(nullptr, 0x7000, 0x8000, 0x1000, PROT_READ, "");
+ MapInfo info(nullptr, nullptr, 0x7000, 0x8000, 0x1000, PROT_READ, "");
for (size_t i = 0; i < kNumConcurrentThreads; i++) {
std::thread* thread = new std::thread([i, this, &wait, &info, &elf_in_threads]() {
while (wait)
@@ -373,8 +373,8 @@
// Verify that previous maps don't automatically get the same elf object.
TEST_F(MapInfoGetElfTest, prev_map_elf_not_set) {
- MapInfo info1(nullptr, 0x1000, 0x2000, 0, PROT_READ, "/not/present");
- MapInfo info2(&info1, 0x2000, 0x3000, 0, PROT_READ, elf_.path);
+ MapInfo info1(nullptr, nullptr, 0x1000, 0x2000, 0, PROT_READ, "/not/present");
+ MapInfo info2(&info1, &info1, 0x2000, 0x3000, 0, PROT_READ, elf_.path);
Elf32_Ehdr ehdr;
TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
@@ -389,8 +389,25 @@
// Verify that a read-only map followed by a read-execute map will result
// in the same elf object in both maps.
TEST_F(MapInfoGetElfTest, read_only_followed_by_read_exec_share_elf) {
- MapInfo r_info(nullptr, 0x1000, 0x2000, 0, PROT_READ, elf_.path);
- MapInfo rw_info(&r_info, 0x2000, 0x3000, 0x1000, PROT_READ | PROT_EXEC, elf_.path);
+ MapInfo r_info(nullptr, nullptr, 0x1000, 0x2000, 0, PROT_READ, elf_.path);
+ MapInfo rw_info(&r_info, &r_info, 0x2000, 0x3000, 0x1000, PROT_READ | PROT_EXEC, elf_.path);
+
+ Elf32_Ehdr ehdr;
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+ memory_->SetMemory(0x1000, &ehdr, sizeof(ehdr));
+ Elf* elf = rw_info.GetElf(process_memory_, ARCH_ARM);
+ ASSERT_TRUE(elf != nullptr);
+ ASSERT_TRUE(elf->valid());
+
+ ASSERT_EQ(elf, r_info.GetElf(process_memory_, ARCH_ARM));
+}
+
+// Verify that a read-only map followed by an empty map, then followed by
+// a read-execute map will result in the same elf object in both maps.
+TEST_F(MapInfoGetElfTest, read_only_followed_by_empty_then_read_exec_share_elf) {
+ MapInfo r_info(nullptr, nullptr, 0x1000, 0x2000, 0, PROT_READ, elf_.path);
+ MapInfo empty(&r_info, &r_info, 0x2000, 0x3000, 0, 0, "");
+ MapInfo rw_info(&empty, &r_info, 0x3000, 0x4000, 0x2000, PROT_READ | PROT_EXEC, elf_.path);
Elf32_Ehdr ehdr;
TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
diff --git a/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
index da3dbf2..971d452 100644
--- a/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
+++ b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
@@ -50,7 +50,7 @@
process_memory_.reset(memory_);
elf_ = new ElfFake(new MemoryFake);
elf_container_.reset(elf_);
- map_info_.reset(new MapInfo(nullptr, 0x1000, 0x20000, 0, PROT_READ | PROT_WRITE, ""));
+ map_info_.reset(new MapInfo(nullptr, nullptr, 0x1000, 0x20000, 0, PROT_READ | PROT_WRITE, ""));
}
void MultipleThreadTest(uint64_t expected_load_bias);
@@ -63,7 +63,7 @@
};
TEST_F(MapInfoGetLoadBiasTest, no_elf_and_no_valid_elf_in_memory) {
- MapInfo info(nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
+ MapInfo info(nullptr, nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
EXPECT_EQ(0U, info.GetLoadBias(process_memory_));
}
diff --git a/libunwindstack/tests/MapInfoTest.cpp b/libunwindstack/tests/MapInfoTest.cpp
index ef76b1b..98edc0e 100644
--- a/libunwindstack/tests/MapInfoTest.cpp
+++ b/libunwindstack/tests/MapInfoTest.cpp
@@ -26,8 +26,8 @@
namespace unwindstack {
TEST(MapInfoTest, maps_constructor_const_char) {
- MapInfo prev_map(nullptr, 0, 0, 0, 0, "");
- MapInfo map_info(&prev_map, 1, 2, 3, 4, "map");
+ MapInfo prev_map(nullptr, nullptr, 0, 0, 0, 0, "");
+ MapInfo map_info(&prev_map, &prev_map, 1, 2, 3, 4, "map");
EXPECT_EQ(&prev_map, map_info.prev_map);
EXPECT_EQ(1UL, map_info.start);
@@ -42,8 +42,8 @@
TEST(MapInfoTest, maps_constructor_string) {
std::string name("string_map");
- MapInfo prev_map(nullptr, 0, 0, 0, 0, "");
- MapInfo map_info(&prev_map, 1, 2, 3, 4, name);
+ MapInfo prev_map(nullptr, nullptr, 0, 0, 0, 0, "");
+ MapInfo map_info(&prev_map, &prev_map, 1, 2, 3, 4, name);
EXPECT_EQ(&prev_map, map_info.prev_map);
EXPECT_EQ(1UL, map_info.start);
@@ -62,7 +62,7 @@
elf->FakeSetInterface(interface);
interface->FakePushFunctionData(FunctionData("function", 1000));
- MapInfo map_info(nullptr, 1, 2, 3, 4, "");
+ MapInfo map_info(nullptr, nullptr, 1, 2, 3, 4, "");
map_info.elf.reset(elf);
std::string name;
diff --git a/libunwindstack/tests/MapsTest.cpp b/libunwindstack/tests/MapsTest.cpp
index 9e7a6ab..724eeb5 100644
--- a/libunwindstack/tests/MapsTest.cpp
+++ b/libunwindstack/tests/MapsTest.cpp
@@ -82,7 +82,7 @@
}
TEST(MapsTest, verify_parse_line) {
- MapInfo info(nullptr, 0, 0, 0, 0, "");
+ MapInfo info(nullptr, nullptr, 0, 0, 0, 0, "");
VerifyLine("01-02 rwxp 03 04:05 06\n", &info);
EXPECT_EQ(1U, info.start);
@@ -155,7 +155,7 @@
}
TEST(MapsTest, verify_large_values) {
- MapInfo info(nullptr, 0, 0, 0, 0, "");
+ MapInfo info(nullptr, nullptr, 0, 0, 0, 0, "");
#if defined(__LP64__)
VerifyLine("fabcdef012345678-f12345678abcdef8 rwxp f0b0d0f010305070 00:00 0\n", &info);
EXPECT_EQ(0xfabcdef012345678UL, info.start);
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
index 472d1cf..0a33e2f 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -182,7 +182,7 @@
RegsX86_64 regs_x86_64;
RegsMips regs_mips;
RegsMips64 regs_mips64;
- MapInfo map_info(nullptr, 0x1000, 0x2000, 0, 0, "");
+ MapInfo map_info(nullptr, nullptr, 0x1000, 0x2000, 0, 0, "");
Elf* invalid_elf = new Elf(nullptr);
map_info.elf.reset(invalid_elf);
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index 364101a..c2bd836 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -169,15 +169,18 @@
};
std::unordered_map<std::string, uint32_t> UnwindOfflineTest::arm64_regs_ = {
- {"x0", ARM64_REG_R0}, {"x1", ARM64_REG_R1}, {"x2", ARM64_REG_R2}, {"x3", ARM64_REG_R3},
- {"x4", ARM64_REG_R4}, {"x5", ARM64_REG_R5}, {"x6", ARM64_REG_R6}, {"x7", ARM64_REG_R7},
- {"x8", ARM64_REG_R8}, {"x9", ARM64_REG_R9}, {"x10", ARM64_REG_R10}, {"x11", ARM64_REG_R11},
- {"x12", ARM64_REG_R12}, {"x13", ARM64_REG_R13}, {"x14", ARM64_REG_R14}, {"x15", ARM64_REG_R15},
- {"x16", ARM64_REG_R16}, {"x17", ARM64_REG_R17}, {"x18", ARM64_REG_R18}, {"x19", ARM64_REG_R19},
- {"x20", ARM64_REG_R20}, {"x21", ARM64_REG_R21}, {"x22", ARM64_REG_R22}, {"x23", ARM64_REG_R23},
- {"x24", ARM64_REG_R24}, {"x25", ARM64_REG_R25}, {"x26", ARM64_REG_R26}, {"x27", ARM64_REG_R27},
- {"x28", ARM64_REG_R28}, {"x29", ARM64_REG_R29}, {"sp", ARM64_REG_SP}, {"lr", ARM64_REG_LR},
- {"pc", ARM64_REG_PC},
+ {"x0", ARM64_REG_R0}, {"x1", ARM64_REG_R1}, {"x2", ARM64_REG_R2},
+ {"x3", ARM64_REG_R3}, {"x4", ARM64_REG_R4}, {"x5", ARM64_REG_R5},
+ {"x6", ARM64_REG_R6}, {"x7", ARM64_REG_R7}, {"x8", ARM64_REG_R8},
+ {"x9", ARM64_REG_R9}, {"x10", ARM64_REG_R10}, {"x11", ARM64_REG_R11},
+ {"x12", ARM64_REG_R12}, {"x13", ARM64_REG_R13}, {"x14", ARM64_REG_R14},
+ {"x15", ARM64_REG_R15}, {"x16", ARM64_REG_R16}, {"x17", ARM64_REG_R17},
+ {"x18", ARM64_REG_R18}, {"x19", ARM64_REG_R19}, {"x20", ARM64_REG_R20},
+ {"x21", ARM64_REG_R21}, {"x22", ARM64_REG_R22}, {"x23", ARM64_REG_R23},
+ {"x24", ARM64_REG_R24}, {"x25", ARM64_REG_R25}, {"x26", ARM64_REG_R26},
+ {"x27", ARM64_REG_R27}, {"x28", ARM64_REG_R28}, {"x29", ARM64_REG_R29},
+ {"sp", ARM64_REG_SP}, {"lr", ARM64_REG_LR}, {"pc", ARM64_REG_PC},
+ {"pst", ARM64_REG_PSTATE},
};
std::unordered_map<std::string, uint32_t> UnwindOfflineTest::x86_regs_ = {
@@ -1697,4 +1700,40 @@
EXPECT_EQ(0xffe67d10ULL, unwinder.frames()[16].sp);
}
+TEST_F(UnwindOfflineTest, empty_arm64) {
+ ASSERT_NO_FATAL_FAILURE(Init("empty_arm64/", ARCH_ARM64));
+
+ Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ unwinder.Unwind();
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(7U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 00000000000963a4 libc.so (__ioctl+4)\n"
+ " #01 pc 000000000005344c libc.so (ioctl+140)\n"
+ " #02 pc 0000000000050ce4 libbinder.so "
+ "(android::IPCThreadState::talkWithDriver(bool)+308)\n"
+ " #03 pc 0000000000050e98 libbinder.so "
+ "(android::IPCThreadState::getAndExecuteCommand()+24)\n"
+ " #04 pc 00000000000516ac libbinder.so (android::IPCThreadState::joinThreadPool(bool)+60)\n"
+ " #05 pc 00000000000443b0 netd (main+1056)\n"
+ " #06 pc 0000000000045594 libc.so (__libc_init+108)\n",
+ frame_info);
+
+ EXPECT_EQ(0x72a02203a4U, unwinder.frames()[0].pc);
+ EXPECT_EQ(0x7ffb6c0b50U, unwinder.frames()[0].sp);
+ EXPECT_EQ(0x72a01dd44cU, unwinder.frames()[1].pc);
+ EXPECT_EQ(0x7ffb6c0b50U, unwinder.frames()[1].sp);
+ EXPECT_EQ(0x729f759ce4U, unwinder.frames()[2].pc);
+ EXPECT_EQ(0x7ffb6c0c50U, unwinder.frames()[2].sp);
+ EXPECT_EQ(0x729f759e98U, unwinder.frames()[3].pc);
+ EXPECT_EQ(0x7ffb6c0ce0U, unwinder.frames()[3].sp);
+ EXPECT_EQ(0x729f75a6acU, unwinder.frames()[4].pc);
+ EXPECT_EQ(0x7ffb6c0d10U, unwinder.frames()[4].sp);
+ EXPECT_EQ(0x5d478af3b0U, unwinder.frames()[5].pc);
+ EXPECT_EQ(0x7ffb6c0d40U, unwinder.frames()[5].sp);
+ EXPECT_EQ(0x72a01cf594U, unwinder.frames()[6].pc);
+ EXPECT_EQ(0x7ffb6c0f30U, unwinder.frames()[6].sp);
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/files/offline/empty_arm64/libbinder.so b/libunwindstack/tests/files/offline/empty_arm64/libbinder.so
new file mode 100644
index 0000000..f30384c
--- /dev/null
+++ b/libunwindstack/tests/files/offline/empty_arm64/libbinder.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/empty_arm64/libc.so b/libunwindstack/tests/files/offline/empty_arm64/libc.so
new file mode 100644
index 0000000..b05dcaf
--- /dev/null
+++ b/libunwindstack/tests/files/offline/empty_arm64/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/empty_arm64/maps.txt b/libunwindstack/tests/files/offline/empty_arm64/maps.txt
new file mode 100644
index 0000000..edb83c6
--- /dev/null
+++ b/libunwindstack/tests/files/offline/empty_arm64/maps.txt
@@ -0,0 +1,9 @@
+5d4786b000-5d47893000 r--p 0 00:00 0 netd
+5d47893000-5d47894000 ---p 0 00:00 0
+5d47894000-5d47901000 --xp 29000 00:00 0 netd
+729f709000-729f750000 r--p 0 00:00 0 libbinder.so
+729f750000-729f751000 ---p 0 00:00 0
+729f751000-729f794000 --xp 48000 00:00 0 libbinder.so
+72a018a000-72a01c2000 r--p 0 00:00 0 libc.so
+72a01c2000-72a01c3000 ---p 0 00:00 0
+72a01c3000-72a023b000 --xp 39000 00:00 0 libc.so
diff --git a/libunwindstack/tests/files/offline/empty_arm64/netd b/libunwindstack/tests/files/offline/empty_arm64/netd
new file mode 100644
index 0000000..8a72e94
--- /dev/null
+++ b/libunwindstack/tests/files/offline/empty_arm64/netd
Binary files differ
diff --git a/libunwindstack/tests/files/offline/empty_arm64/regs.txt b/libunwindstack/tests/files/offline/empty_arm64/regs.txt
new file mode 100644
index 0000000..3d4279f
--- /dev/null
+++ b/libunwindstack/tests/files/offline/empty_arm64/regs.txt
@@ -0,0 +1,34 @@
+x0: 1d
+x1: c0306201
+x2: 7ffb6c0c50
+x3: 0
+x4: 0
+x5: 0
+x6: 0
+x7: 0
+x8: 1d
+x9: 7ffb6c0c00
+x10: 7ffb6c0c50
+x11: 7ffb6c0bd0
+x12: ffffff80ffffffd0
+x13: 0
+x14: 72a0240ce2
+x15: 20
+x16: 729f7a54e8
+x17: 72a01dd3c0
+x18: 72a0ac2000
+x19: 72a0666000
+x20: 719769b610
+x21: 719769b730
+x22: c0306201
+x23: fffffff7
+x24: 72a0666000
+x25: 0
+x26: 0
+x27: 0
+x28: 0
+x29: 7ffb6c0c30
+sp: 7ffb6c0b50
+lr: 72a01dd450
+pc: 72a02203a4
+pst: a0000000
diff --git a/libunwindstack/tests/files/offline/empty_arm64/stack.data b/libunwindstack/tests/files/offline/empty_arm64/stack.data
new file mode 100644
index 0000000..6d6108c
--- /dev/null
+++ b/libunwindstack/tests/files/offline/empty_arm64/stack.data
Binary files differ
diff --git a/libutils/StrongPointer_test.cpp b/libutils/StrongPointer_test.cpp
index 153cf96..7b2e37f 100644
--- a/libutils/StrongPointer_test.cpp
+++ b/libutils/StrongPointer_test.cpp
@@ -56,3 +56,18 @@
}
ASSERT_TRUE(isDeleted) << "foo was leaked!";
}
+
+TEST(StrongPointer, NullptrComparison) {
+ sp<SPFoo> foo;
+ ASSERT_EQ(foo, nullptr);
+ ASSERT_EQ(nullptr, foo);
+}
+
+TEST(StrongPointer, PointerComparison) {
+ bool isDeleted;
+ sp<SPFoo> foo = new SPFoo(&isDeleted);
+ ASSERT_EQ(foo.get(), foo);
+ ASSERT_EQ(foo, foo.get());
+ ASSERT_NE(nullptr, foo);
+ ASSERT_NE(foo, nullptr);
+}
diff --git a/libutils/include/utils/Flattenable.h b/libutils/include/utils/Flattenable.h
index 953b859..17c5e10 100644
--- a/libutils/include/utils/Flattenable.h
+++ b/libutils/include/utils/Flattenable.h
@@ -52,7 +52,12 @@
template<size_t N>
static size_t align(void*& buffer) {
- return align<N>( const_cast<void const*&>(buffer) );
+ static_assert(!(N & (N - 1)), "Can only align to a power of 2.");
+ void* b = buffer;
+ buffer = reinterpret_cast<void*>((uintptr_t(buffer) + (N-1)) & ~(N-1));
+ size_t delta = size_t(uintptr_t(buffer) - uintptr_t(b));
+ memset(b, 0, delta);
+ return delta;
}
static void advance(void*& buffer, size_t& size, size_t offset) {
diff --git a/libutils/include/utils/StrongPointer.h b/libutils/include/utils/StrongPointer.h
index 100e507..6f4fb47 100644
--- a/libutils/include/utils/StrongPointer.h
+++ b/libutils/include/utils/StrongPointer.h
@@ -27,43 +27,6 @@
// ---------------------------------------------------------------------------
-// TODO: Maybe remove sp<> ? wp<> comparison? These are dangerous: If the wp<>
-// was created before the sp<>, and they point to different objects, they may
-// compare equal even if they are entirely unrelated. E.g. CameraService
-// currently performa such comparisons.
-
-#define COMPARE_STRONG(_op_) \
-template<typename U> \
-inline bool operator _op_ (const sp<U>& o) const { \
- return m_ptr _op_ o.m_ptr; \
-} \
-template<typename U> \
-inline bool operator _op_ (const U* o) const { \
- return m_ptr _op_ o; \
-} \
-/* Needed to handle type inference for nullptr: */ \
-inline bool operator _op_ (const T* o) const { \
- return m_ptr _op_ o; \
-}
-
-template<template<typename C> class comparator, typename T, typename U>
-static inline bool _sp_compare_(T* a, U* b) {
- return comparator<typename std::common_type<T*, U*>::type>()(a, b);
-}
-
-// Use std::less and friends to avoid undefined behavior when ordering pointers
-// to different objects.
-#define COMPARE_STRONG_FUNCTIONAL(_op_, _compare_) \
-template<typename U> \
-inline bool operator _op_ (const sp<U>& o) const { \
- return _sp_compare_<_compare_>(m_ptr, o.m_ptr); \
-} \
-template<typename U> \
-inline bool operator _op_ (const U* o) const { \
- return _sp_compare_<_compare_>(m_ptr, o); \
-}
-// ---------------------------------------------------------------------------
-
template<typename T>
class sp {
public:
@@ -102,15 +65,6 @@
inline T* get() const { return m_ptr; }
inline explicit operator bool () const { return m_ptr != nullptr; }
- // Operators
-
- COMPARE_STRONG(==)
- COMPARE_STRONG(!=)
- COMPARE_STRONG_FUNCTIONAL(>, std::greater)
- COMPARE_STRONG_FUNCTIONAL(<, std::less)
- COMPARE_STRONG_FUNCTIONAL(<=, std::less_equal)
- COMPARE_STRONG_FUNCTIONAL(>=, std::greater_equal)
-
// Punt these to the wp<> implementation.
template<typename U>
inline bool operator == (const wp<U>& o) const {
@@ -130,13 +84,69 @@
T* m_ptr;
};
-// For code size reasons, we do not want these inlined or templated.
-void sp_report_race();
-void sp_report_stack_pointer();
+#define COMPARE_STRONG(_op_) \
+ template <typename T, typename U> \
+ static inline bool operator _op_(const sp<T>& t, const sp<U>& u) { \
+ return t.get() _op_ u.get(); \
+ } \
+ template <typename T, typename U> \
+ static inline bool operator _op_(const T* t, const sp<U>& u) { \
+ return t _op_ u.get(); \
+ } \
+ template <typename T, typename U> \
+ static inline bool operator _op_(const sp<T>& t, const U* u) { \
+ return t.get() _op_ u; \
+ } \
+ template <typename T> \
+ static inline bool operator _op_(const sp<T>& t, std::nullptr_t) { \
+ return t.get() _op_ nullptr; \
+ } \
+ template <typename T> \
+ static inline bool operator _op_(std::nullptr_t, const sp<T>& t) { \
+ return nullptr _op_ t.get(); \
+ }
+
+template <template <typename C> class comparator, typename T, typename U>
+static inline bool _sp_compare_(T* a, U* b) {
+ return comparator<typename std::common_type<T*, U*>::type>()(a, b);
+}
+
+#define COMPARE_STRONG_FUNCTIONAL(_op_, _compare_) \
+ template <typename T, typename U> \
+ static inline bool operator _op_(const sp<T>& t, const sp<U>& u) { \
+ return _sp_compare_<_compare_>(t.get(), u.get()); \
+ } \
+ template <typename T, typename U> \
+ static inline bool operator _op_(const T* t, const sp<U>& u) { \
+ return _sp_compare_<_compare_>(t, u.get()); \
+ } \
+ template <typename T, typename U> \
+ static inline bool operator _op_(const sp<T>& t, const U* u) { \
+ return _sp_compare_<_compare_>(t.get(), u); \
+ } \
+ template <typename T> \
+ static inline bool operator _op_(const sp<T>& t, std::nullptr_t) { \
+ return _sp_compare_<_compare_>(t.get(), nullptr); \
+ } \
+ template <typename T> \
+ static inline bool operator _op_(std::nullptr_t, const sp<T>& t) { \
+ return _sp_compare_<_compare_>(nullptr, t.get()); \
+ }
+
+COMPARE_STRONG(==)
+COMPARE_STRONG(!=)
+COMPARE_STRONG_FUNCTIONAL(>, std::greater)
+COMPARE_STRONG_FUNCTIONAL(<, std::less)
+COMPARE_STRONG_FUNCTIONAL(<=, std::less_equal)
+COMPARE_STRONG_FUNCTIONAL(>=, std::greater_equal)
#undef COMPARE_STRONG
#undef COMPARE_STRONG_FUNCTIONAL
+// For code size reasons, we do not want these inlined or templated.
+void sp_report_race();
+void sp_report_stack_pointer();
+
// ---------------------------------------------------------------------------
// No user serviceable parts below here.
diff --git a/libvndksupport/Android.bp b/libvndksupport/Android.bp
index f4544a1..b92c76c 100644
--- a/libvndksupport/Android.bp
+++ b/libvndksupport/Android.bp
@@ -1,5 +1,3 @@
-subdirs = ["tests"]
-
cc_library {
name: "libvndksupport",
native_bridge_supported: true,
diff --git a/llkd/libllkd.cpp b/llkd/libllkd.cpp
index b26ad4d..1c3acb8 100644
--- a/llkd/libllkd.cpp
+++ b/llkd/libllkd.cpp
@@ -304,10 +304,13 @@
bool cmdlineValid; // cmdline has been cached
bool updated; // cleared before monitoring pass.
bool killed; // sent a kill to this thread, next panic...
+ bool frozen; // process is in frozen cgroup.
void setComm(const char* _comm) { strncpy(comm + 1, _comm, sizeof(comm) - 2); }
- proc(pid_t tid, pid_t pid, pid_t ppid, const char* _comm, int time, char state)
+ void setFrozen(bool _frozen) { frozen = _frozen; }
+
+ proc(pid_t tid, pid_t pid, pid_t ppid, const char* _comm, int time, char state, bool frozen)
: tid(tid),
schedUpdate(0),
nrSwitches(0),
@@ -327,7 +330,8 @@
exeMissingValid(false),
cmdlineValid(false),
updated(true),
- killed(!llkTestWithKill) {
+ killed(!llkTestWithKill),
+ frozen(frozen) {
memset(comm, '\0', sizeof(comm));
setComm(_comm);
}
@@ -373,6 +377,8 @@
return uid;
}
+ bool isFrozen() { return frozen; }
+
void reset(void) { // reset cache, if we detected pid rollover
uid = -1;
state = '?';
@@ -592,8 +598,9 @@
tids.erase(tid);
}
-proc* llkTidAlloc(pid_t tid, pid_t pid, pid_t ppid, const char* comm, int time, char state) {
- auto it = tids.emplace(std::make_pair(tid, proc(tid, pid, ppid, comm, time, state)));
+proc* llkTidAlloc(pid_t tid, pid_t pid, pid_t ppid, const char* comm, int time, char state,
+ bool frozen) {
+ auto it = tids.emplace(std::make_pair(tid, proc(tid, pid, ppid, comm, time, state, frozen)));
return &it.first->second;
}
@@ -1039,12 +1046,18 @@
continue;
}
+ // Get the process cgroup
+ auto cgroup = ReadFile(piddir + "/cgroup");
+ auto frozen = cgroup.find(":freezer:/frozen") != std::string::npos;
+
auto procp = llkTidLookup(tid);
if (procp == nullptr) {
- procp = llkTidAlloc(tid, pid, ppid, pdir, utime + stime, state);
+ procp = llkTidAlloc(tid, pid, ppid, pdir, utime + stime, state, frozen);
} else {
// comm can change ...
procp->setComm(pdir);
+ // frozen can change, too...
+ procp->setFrozen(frozen);
procp->updated = true;
// pid/ppid/tid wrap?
if (((procp->update != prevUpdate) && (procp->update != llkUpdate)) ||
@@ -1084,6 +1097,9 @@
if ((tid == myTid) || llkSkipPid(tid)) {
continue;
}
+ if (procp->isFrozen()) {
+ break;
+ }
if (llkSkipPpid(ppid)) {
break;
}
@@ -1101,7 +1117,7 @@
auto pprocp = llkTidLookup(ppid);
if (pprocp == nullptr) {
- pprocp = llkTidAlloc(ppid, ppid, 0, "", 0, '?');
+ pprocp = llkTidAlloc(ppid, ppid, 0, "", 0, '?', false);
}
if (pprocp) {
if (llkSkipPproc(pprocp, procp)) break;
diff --git a/llkd/llkd-debuggable.rc b/llkd/llkd-debuggable.rc
index 724cb5e..4b11b1c 100644
--- a/llkd/llkd-debuggable.rc
+++ b/llkd/llkd-debuggable.rc
@@ -13,7 +13,7 @@
disabled
user llkd
group llkd readproc
- capabilities KILL IPC_LOCK SYS_PTRACE DAC_OVERRIDE
+ capabilities KILL IPC_LOCK SYS_PTRACE DAC_OVERRIDE SYS_ADMIN
file /dev/kmsg w
file /proc/sysrq-trigger w
writepid /dev/cpuset/system-background/tasks
diff --git a/llkd/tests/llkd_test.cpp b/llkd/tests/llkd_test.cpp
index 96079cc..475512c 100644
--- a/llkd/tests/llkd_test.cpp
+++ b/llkd/tests/llkd_test.cpp
@@ -89,7 +89,8 @@
rest();
std::string setprop("setprop ");
// Manually check that SyS_openat is _added_ to the list when restarted
- execute((setprop + LLK_CHECK_STACK_PROPERTY + " ,SyS_openat").c_str());
+ // 4.19+ kernels report __arm64_sys_openat b/147486902
+ execute((setprop + LLK_CHECK_STACK_PROPERTY + " ,SyS_openat,__arm64_sys_openat").c_str());
rest();
execute((setprop + LLK_ENABLE_WRITEABLE_PROPERTY + " false").c_str());
rest();
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 7b18438..08e3d22 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -110,7 +110,7 @@
#endif
static int openLogFile(const char* pathname, size_t sizeKB) {
- int fd = open(pathname, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
+ int fd = open(pathname, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR | S_IRGRP);
if (fd < 0) {
return fd;
}
diff --git a/logcat/logcatd b/logcat/logcatd
index 622e567..5a1415d 100755
--- a/logcat/logcatd
+++ b/logcat/logcatd
@@ -4,6 +4,10 @@
# first reads the 'last' logcat to persistent storage with `-L` then run logcat again without
# `-L` to read the current logcat buffers to persistent storage.
+# init sets the umask to 077 for forked processes. logpersist needs to create files that are group
+# readable. So relax the umask to only disallow group wx and world rwx.
+umask 037
+
has_last="false"
for arg in "$@"; do
if [ "$arg" == "-L" -o "$arg" == "--last" ]; then
diff --git a/logcat/logcatd.rc b/logcat/logcatd.rc
index e986184..64d5500 100644
--- a/logcat/logcatd.rc
+++ b/logcat/logcatd.rc
@@ -30,8 +30,8 @@
setprop logd.logpersistd.enable true
on property:logd.logpersistd.enable=true && property:logd.logpersistd=logcatd
- # all exec/services are called with umask(077), so no gain beyond 0700
- mkdir /data/misc/logd 0700 logd log
+ # log group should be able to read persisted logs
+ mkdir /data/misc/logd 0750 logd log
start logcatd
# stop logcatd service and clear data
diff --git a/logd/logtagd.rc b/logd/logtagd.rc
index 46aa8c1..248a78c 100644
--- a/logd/logtagd.rc
+++ b/logd/logtagd.rc
@@ -2,7 +2,7 @@
# logtagd event log tag service (debug only)
#
on post-fs-data
- mkdir /data/misc/logd 0700 logd log
+ mkdir /data/misc/logd 0750 logd log
write /data/misc/logd/event-log-tags ""
chown logd log /data/misc/logd/event-log-tags
chmod 0600 /data/misc/logd/event-log-tags
diff --git a/property_service/Android.bp b/property_service/Android.bp
deleted file mode 100644
index b44c296..0000000
--- a/property_service/Android.bp
+++ /dev/null
@@ -1 +0,0 @@
-subdirs = ["*"]
diff --git a/property_service/property_info_checker/Android.bp b/property_service/property_info_checker/Android.bp
index 7d66199..65e660a 100644
--- a/property_service/property_info_checker/Android.bp
+++ b/property_service/property_info_checker/Android.bp
@@ -7,6 +7,7 @@
"libpropertyinfoserializer",
"libpropertyinfoparser",
"libbase",
+ "liblog",
"libsepol",
],
srcs: ["property_info_checker.cpp"],
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 5821379..a9d0ed0 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -67,6 +67,11 @@
EXPORT_GLOBAL_GCOV_OPTIONS := export GCOV_PREFIX /data/misc/trace
endif
+EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS :=
+ifeq ($(CLANG_COVERAGE),true)
+ EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS := export LLVM_PROFILE_FILE /data/misc/trace/clang-%p-%m.profraw
+endif
+
# Put it here instead of in init.rc module definition,
# because init.rc is conditionally included.
#
@@ -147,6 +152,7 @@
$(hide) sed -i -e 's?%SYSTEMSERVERCLASSPATH%?$(PRODUCT_SYSTEM_SERVER_CLASSPATH)?g' $@
$(hide) sed -i -e 's?%EXPORT_GLOBAL_ASAN_OPTIONS%?$(EXPORT_GLOBAL_ASAN_OPTIONS)?g' $@
$(hide) sed -i -e 's?%EXPORT_GLOBAL_GCOV_OPTIONS%?$(EXPORT_GLOBAL_GCOV_OPTIONS)?g' $@
+ $(hide) sed -i -e 's?%EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS%?$(EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS)?g' $@
$(hide) sed -i -e 's?%EXPORT_GLOBAL_HWASAN_OPTIONS%?$(EXPORT_GLOBAL_HWASAN_OPTIONS)?g' $@
# Append PLATFORM_VNDK_VERSION to base name.
diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in
index 50005d9..fdaaf1a 100644
--- a/rootdir/init.environ.rc.in
+++ b/rootdir/init.environ.rc.in
@@ -15,4 +15,5 @@
export SYSTEMSERVERCLASSPATH %SYSTEMSERVERCLASSPATH%
%EXPORT_GLOBAL_ASAN_OPTIONS%
%EXPORT_GLOBAL_GCOV_OPTIONS%
+ %EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS%
%EXPORT_GLOBAL_HWASAN_OPTIONS%
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 7a3339d..e575808 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -42,6 +42,10 @@
mkdir /linkerconfig/bootstrap 0755
mkdir /linkerconfig/default 0755
+ # Disable dm-verity hash prefetching, since it doesn't help performance
+ # Read more in b/136247322
+ write /sys/module/dm_verity/parameters/prefetch_cluster 0
+
# Generate ld.config.txt for early executed processes
exec -- /system/bin/linkerconfig --target /linkerconfig/bootstrap
chmod 644 /linkerconfig/bootstrap/ld.config.txt
@@ -169,7 +173,7 @@
mkdir /mnt/user/0/emulated/0 0755 root root
# Prepare directories for pass through processes
- mkdir /mnt/pass_through 0755 root root
+ mkdir /mnt/pass_through 0700 root root
mkdir /mnt/pass_through/0 0755 root root
mkdir /mnt/pass_through/0/self 0755 root root
mkdir /mnt/pass_through/0/emulated 0755 root root
@@ -547,6 +551,7 @@
chown bluetooth bluetooth /data/misc/bluedroid/bt_config.conf
mkdir /data/misc/bluetooth 0770 bluetooth bluetooth
mkdir /data/misc/bluetooth/logs 0770 bluetooth bluetooth
+ mkdir /data/misc/credstore 0700 credstore credstore
mkdir /data/misc/keystore 0700 keystore keystore
mkdir /data/misc/gatekeeper 0700 system system
mkdir /data/misc/keychain 0771 system system
@@ -584,11 +589,11 @@
# profile file layout
mkdir /data/misc/profiles 0771 system system
mkdir /data/misc/profiles/cur 0771 system system
- mkdir /data/misc/profiles/ref 0771 system system
+ mkdir /data/misc/profiles/ref 0770 system system
mkdir /data/misc/profman 0770 system shell
mkdir /data/misc/gcov 0770 root root
mkdir /data/misc/installd 0700 root root
- mkdir /data/misc/apexdata 0700 root root
+ mkdir /data/misc/apexdata 0711 root root
mkdir /data/misc/apexrollback 0700 root root
mkdir /data/preloads 0775 system system encryption=None
@@ -691,6 +696,10 @@
mount none /data/user /data_mirror/data_ce/null bind rec
mount none /data/user_de /data_mirror/data_de/null bind rec
+ # Create mirror directory for jit profiles
+ mkdir /data_mirror/cur_profiles 0700 root root
+ mount none /data/misc/profiles/cur /data_mirror/cur_profiles bind rec
+
mkdir /data/cache 0770 system cache encryption=Require
mkdir /data/cache/recovery 0770 system cache
mkdir /data/cache/backup_stage 0700 system system
@@ -701,7 +710,7 @@
mkdir /data/rollback-observer 0700 system system encryption=DeleteIfNecessary
# Create root dir for Incremental Service
- mkdir /data/incremental 0770 system system encryption=None
+ mkdir /data/incremental 0771 system system encryption=Require
# Wait for apexd to finish activating APEXes before starting more processes.
wait_for_prop apexd.status ready
@@ -936,14 +945,33 @@
on property:sys.sysctl.tcp_def_init_rwnd=*
write /proc/sys/net/ipv4/tcp_default_init_rwnd ${sys.sysctl.tcp_def_init_rwnd}
-on property:security.perf_harden=0
+# perf_event_open syscall security:
+# Newer kernels have the ability to control the use of the syscall via SELinux
+# hooks. init tests for this, and sets sys_init.perf_lsm_hooks to 1 if the
+# kernel has the hooks. In this case, the system-wide perf_event_paranoid
+# sysctl is set to -1 (unrestricted use), and the SELinux policy is used for
+# controlling access. On older kernels, the paranoid value is the only means of
+# controlling access. It is normally 3 (allow only root), but the shell user
+# can lower it to 1 (allowing thread-scoped pofiling) via security.perf_harden.
+on property:sys.init.perf_lsm_hooks=1
+ write /proc/sys/kernel/perf_event_paranoid -1
+on property:security.perf_harden=0 && property:sys.init.perf_lsm_hooks=""
write /proc/sys/kernel/perf_event_paranoid 1
+on property:security.perf_harden=1 && property:sys.init.perf_lsm_hooks=""
+ write /proc/sys/kernel/perf_event_paranoid 3
+
+# Additionally, simpleperf profiler uses debug.* and security.perf_harden
+# sysprops to be able to indirectly set these sysctls.
+on property:security.perf_harden=0
write /proc/sys/kernel/perf_event_max_sample_rate ${debug.perf_event_max_sample_rate:-100000}
write /proc/sys/kernel/perf_cpu_time_max_percent ${debug.perf_cpu_time_max_percent:-25}
write /proc/sys/kernel/perf_event_mlock_kb ${debug.perf_event_mlock_kb:-516}
-
+# Default values.
on property:security.perf_harden=1
- write /proc/sys/kernel/perf_event_paranoid 3
+ write /proc/sys/kernel/perf_event_max_sample_rate 100000
+ write /proc/sys/kernel/perf_cpu_time_max_percent 25
+ write /proc/sys/kernel/perf_event_mlock_kb 516
+
# on shutdown
# In device's init.rc, this trigger can be used to do device-specific actions
diff --git a/shell_and_utilities/README.md b/shell_and_utilities/README.md
index d391cc1..3bee875 100644
--- a/shell_and_utilities/README.md
+++ b/shell_and_utilities/README.md
@@ -218,25 +218,28 @@
bzip2: bzcat bzip2 bunzip2
+gavinhoward/bc: bc
+
one-true-awk: awk
toolbox: getevent getprop setprop start stop
-toybox: acpi base64 basename bc blkid blockdev cal cat chattr chcon chgrp
-chmod chown chroot chrt cksum clear cmp comm cp cpio cut date dd df
-diff dirname dmesg dos2unix du echo egrep env expand expr fallocate
-false fgrep file find flock fmt free freeramdisk fsfreeze getconf
-getenforce getfattr grep groups gunzip gzip head help hostname hwclock
-i2cdetect i2cdump i2cget i2cset iconv id ifconfig inotifyd insmod
-install ionice iorenice iotop kill killall ln load\_policy log logname
-losetup ls lsattr lsmod lsof lspci lsusb makedevs md5sum microcom
+toybox: acpi base64 basename blkid blockdev cal cat chattr chcon chgrp chmod
+chown chroot chrt cksum clear cmp comm cp cpio cut date dd devmem
+df diff dirname dmesg dos2unix du echo egrep env expand expr fallocate
+false fgrep file find flock fmt free freeramdisk fsfreeze fsync getconf
+getenforce getfattr getopt grep groups gunzip gzip head help hostname
+hwclock i2cdetect i2cdump i2cget i2cset iconv id ifconfig inotifyd
+insmod install ionice iorenice iotop kill killall ln load\_policy log
+logname losetup ls lsattr lsmod lsof lspci lsusb makedevs md5sum microcom
mkdir mkfifo mknod mkswap mktemp modinfo modprobe more mount mountpoint
mv nbd-client nc netcat netstat nice nl nohup nproc nsenter od partprobe
paste patch pgrep pidof ping ping6 pivot\_root pkill pmap printenv
-printf prlimit ps pwd pwdx readlink realpath renice restorecon rev
-rfkill rm rmdir rmmod runcon sed sendevent seq setenforce setfattr
-setsid sha1sum sha224sum sha256sum sha384sum sha512sum sleep sort split
-stat strings stty swapoff swapon sync sysctl tac tail tar taskset tee
-time timeout top touch tr traceroute traceroute6 true truncate tty tunctl
-ulimit umount uname uniq unix2dos unlink unshare uptime usleep uudecode
-uuencode uuidgen vconfig vmstat watch wc which whoami xargs xxd yes zcat
+printf prlimit ps pwd pwdx readelf readlink realpath renice restorecon
+rev rfkill rm rmdir rmmod runcon sed sendevent seq setenforce setfattr
+setsid sha1sum sha224sum sha256sum sha384sum sha512sum sleep sort
+split stat strings stty swapoff swapon sync sysctl tac tail tar taskset
+tee time timeout top touch tr traceroute traceroute6 true truncate
+tty tunctl ulimit umount uname uniq unix2dos unlink unshare uptime
+usleep uudecode uuencode uuidgen vconfig vi vmstat watch wc which
+whoami xargs xxd yes zcat
diff --git a/storaged/main.cpp b/storaged/main.cpp
index 3817fb5..e35bd6f 100644
--- a/storaged/main.cpp
+++ b/storaged/main.cpp
@@ -51,7 +51,7 @@
storaged_sp->init();
storaged_sp->report_storage_info();
- LOG_TO(SYSTEM, INFO) << "storaged: Start";
+ LOG(INFO) << "storaged: Start";
for (;;) {
storaged_sp->event_checked();
@@ -76,6 +76,8 @@
bool flag_dump_perf = false;
int opt;
+ android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));
+
for (;;) {
int opt_idx = 0;
static struct option long_options[] = {
@@ -124,13 +126,13 @@
pthread_t storaged_main_thread;
errno = pthread_create(&storaged_main_thread, NULL, storaged_main, NULL);
if (errno != 0) {
- PLOG_TO(SYSTEM, ERROR) << "Failed to create main thread";
+ PLOG(ERROR) << "Failed to create main thread";
return -1;
}
if (StoragedService::start() != android::OK ||
StoragedPrivateService::start() != android::OK) {
- PLOG_TO(SYSTEM, ERROR) << "Failed to start storaged service";
+ PLOG(ERROR) << "Failed to start storaged service";
return -1;
}
diff --git a/storaged/storaged.cpp b/storaged/storaged.cpp
index 1d934a2..573b8c5 100644
--- a/storaged/storaged.cpp
+++ b/storaged/storaged.cpp
@@ -97,25 +97,23 @@
health = get_health_service();
if (health == NULL) {
- LOG_TO(SYSTEM, WARNING) << "health: failed to find IHealth service";
+ LOG(WARNING) << "health: failed to find IHealth service";
return;
}
BatteryStatus status = BatteryStatus::UNKNOWN;
auto ret = health->getChargeStatus([&](Result r, BatteryStatus v) {
if (r != Result::SUCCESS) {
- LOG_TO(SYSTEM, WARNING)
- << "health: cannot get battery status " << toString(r);
+ LOG(WARNING) << "health: cannot get battery status " << toString(r);
return;
}
if (v == BatteryStatus::UNKNOWN) {
- LOG_TO(SYSTEM, WARNING) << "health: invalid battery status";
+ LOG(WARNING) << "health: invalid battery status";
}
status = v;
});
if (!ret.isOk()) {
- LOG_TO(SYSTEM, WARNING) << "health: get charge status transaction error "
- << ret.description();
+ LOG(WARNING) << "health: get charge status transaction error " << ret.description();
}
mUidm.init(is_charger_on(status));
@@ -126,11 +124,11 @@
void storaged_t::serviceDied(uint64_t cookie, const wp<::android::hidl::base::V1_0::IBase>& who) {
if (health != NULL && interfacesEqual(health, who.promote())) {
- LOG_TO(SYSTEM, ERROR) << "health service died, exiting";
+ LOG(ERROR) << "health service died, exiting";
android::hardware::IPCThreadState::self()->stopProcess();
exit(1);
} else {
- LOG_TO(SYSTEM, ERROR) << "unknown service died";
+ LOG(ERROR) << "unknown service died";
}
}
@@ -192,7 +190,7 @@
reinterpret_cast<const Bytef*>(uid_io_usage.SerializeAsString().c_str()),
uid_io_usage.ByteSize());
if (proto.crc() != computed_crc) {
- LOG_TO(SYSTEM, WARNING) << "CRC mismatch in " << proto_file;
+ LOG(WARNING) << "CRC mismatch in " << proto_file;
return;
}
@@ -228,8 +226,7 @@
char* data = nullptr;
if (posix_memalign(reinterpret_cast<void**>(&data),
pagesize, proto->ByteSize())) {
- PLOG_TO(SYSTEM, ERROR) << "Faied to alloc aligned buffer (size: "
- << proto->ByteSize() << ")";
+ PLOG(ERROR) << "Faied to alloc aligned buffer (size: " << proto->ByteSize() << ")";
return data;
}
@@ -246,7 +243,7 @@
(user_id == USER_SYSTEM ? O_DIRECT : 0),
S_IRUSR | S_IWUSR)));
if (fd == -1) {
- PLOG_TO(SYSTEM, ERROR) << "Faied to open tmp file: " << tmp_file;
+ PLOG(ERROR) << "Faied to open tmp file: " << tmp_file;
return;
}
@@ -261,7 +258,7 @@
start = steady_clock::now();
ret = write(fd, data, MIN(benchmark_unit_size, size));
if (ret <= 0) {
- PLOG_TO(SYSTEM, ERROR) << "Faied to write tmp file: " << tmp_file;
+ PLOG(ERROR) << "Faied to write tmp file: " << tmp_file;
return;
}
end = steady_clock::now();
@@ -284,7 +281,7 @@
}
} else {
if (!WriteFully(fd, data, size)) {
- PLOG_TO(SYSTEM, ERROR) << "Faied to write tmp file: " << tmp_file;
+ PLOG(ERROR) << "Faied to write tmp file: " << tmp_file;
return;
}
}
@@ -343,22 +340,21 @@
if (mConfig.event_time_check_usec &&
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start_ts) < 0) {
check_time = false;
- PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
+ PLOG(ERROR) << "clock_gettime() failed";
}
event();
if (mConfig.event_time_check_usec && check_time) {
if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end_ts) < 0) {
- PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
+ PLOG(ERROR) << "clock_gettime() failed";
return;
}
int64_t cost = (end_ts.tv_sec - start_ts.tv_sec) * SEC_TO_USEC +
(end_ts.tv_nsec - start_ts.tv_nsec) / USEC_TO_NSEC;
if (cost > mConfig.event_time_check_usec) {
- LOG_TO(SYSTEM, ERROR)
- << "event loop spent " << cost << " usec, threshold "
- << mConfig.event_time_check_usec << " usec";
+ LOG(ERROR) << "event loop spent " << cost << " usec, threshold "
+ << mConfig.event_time_check_usec << " usec";
}
}
}
diff --git a/storaged/storaged_diskstats.cpp b/storaged/storaged_diskstats.cpp
index 8b5001d..52bd4e0 100644
--- a/storaged/storaged_diskstats.cpp
+++ b/storaged/storaged_diskstats.cpp
@@ -41,8 +41,8 @@
// skip if the input structure are all zeros
if (perf == NULL || perf->is_zero()) return;
- LOG_TO(SYSTEM, INFO) << "disk_perf " << type
- << " rd: " << perf->read_perf << " kbps, " << perf->read_ios << " iops"
+ LOG(INFO) << "disk_perf " << type << " rd: " << perf->read_perf << " kbps, " << perf->read_ios
+ << " iops"
<< " wr: " << perf->write_perf << " kbps, " << perf->write_ios << " iops"
<< " q: " << perf->queue;
}
@@ -71,7 +71,7 @@
// when system is running.
int ret = clock_gettime(CLOCK_MONOTONIC, ts);
if (ret < 0) {
- PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
+ PLOG(ERROR) << "clock_gettime() failed";
return false;
}
return true;
@@ -93,7 +93,7 @@
std::string buffer;
if (!android::base::ReadFileToString(disk_stats_path, &buffer)) {
- PLOG_TO(SYSTEM, ERROR) << disk_stats_path << ": ReadFileToString failed.";
+ PLOG(ERROR) << disk_stats_path << ": ReadFileToString failed.";
return false;
}
@@ -130,12 +130,12 @@
bool success = false;
auto ret = service->getDiskStats([&success, stats](auto result, const auto& halStats) {
if (result == Result::NOT_SUPPORTED) {
- LOG_TO(SYSTEM, DEBUG) << "getDiskStats is not supported on health HAL.";
+ LOG(DEBUG) << "getDiskStats is not supported on health HAL.";
return;
}
if (result != Result::SUCCESS || halStats.size() == 0) {
- LOG_TO(SYSTEM, ERROR) << "getDiskStats failed with result " << toString(result)
- << " and size " << halStats.size();
+ LOG(ERROR) << "getDiskStats failed with result " << toString(result) << " and size "
+ << halStats.size();
return;
}
@@ -144,7 +144,7 @@
});
if (!ret.isOk()) {
- LOG_TO(SYSTEM, ERROR) << "getDiskStats failed with " << ret.description();
+ LOG(ERROR) << "getDiskStats failed with " << ret.description();
return false;
}
@@ -199,9 +199,9 @@
void add_disk_stats(struct disk_stats* src, struct disk_stats* dst)
{
if (dst->end_time != 0 && dst->end_time != src->start_time) {
- LOG_TO(SYSTEM, WARNING) << "Two dis-continuous periods of diskstats"
- << " are added. dst end with " << dst->end_time
- << ", src start with " << src->start_time;
+ LOG(WARNING) << "Two dis-continuous periods of diskstats"
+ << " are added. dst end with " << dst->end_time << ", src start with "
+ << src->start_time;
}
*dst += *src;
diff --git a/storaged/storaged_info.cpp b/storaged/storaged_info.cpp
index 6668cf3..bb21829 100644
--- a/storaged/storaged_info.cpp
+++ b/storaged/storaged_info.cpp
@@ -76,7 +76,7 @@
if (!perf_history.has_day_start_sec() ||
perf_history.daily_perf_size() > (int)daily_perf.size() ||
perf_history.weekly_perf_size() > (int)weekly_perf.size()) {
- LOG_TO(SYSTEM, ERROR) << "Invalid IOPerfHistory proto";
+ LOG(ERROR) << "Invalid IOPerfHistory proto";
return;
}
@@ -114,7 +114,7 @@
{
struct statvfs buf;
if (statvfs(userdata_path.c_str(), &buf) != 0) {
- PLOG_TO(SYSTEM, WARNING) << "Failed to get userdata info";
+ PLOG(WARNING) << "Failed to get userdata info";
return;
}
@@ -328,12 +328,12 @@
void health_storage_info_t::report() {
auto ret = mHealth->getStorageInfo([this](auto result, const auto& halInfos) {
if (result == Result::NOT_SUPPORTED) {
- LOG_TO(SYSTEM, DEBUG) << "getStorageInfo is not supported on health HAL.";
+ LOG(DEBUG) << "getStorageInfo is not supported on health HAL.";
return;
}
if (result != Result::SUCCESS || halInfos.size() == 0) {
- LOG_TO(SYSTEM, ERROR) << "getStorageInfo failed with result " << toString(result)
- << " and size " << halInfos.size();
+ LOG(ERROR) << "getStorageInfo failed with result " << toString(result) << " and size "
+ << halInfos.size();
return;
}
set_values_from_hal_storage_info(halInfos[0]);
@@ -341,7 +341,7 @@
});
if (!ret.isOk()) {
- LOG_TO(SYSTEM, ERROR) << "getStorageInfo failed with " << ret.description();
+ LOG(ERROR) << "getStorageInfo failed with " << ret.description();
}
}
diff --git a/storaged/storaged_uid_monitor.cpp b/storaged/storaged_uid_monitor.cpp
index 55380ba..f47bf72 100644
--- a/storaged/storaged_uid_monitor.cpp
+++ b/storaged/storaged_uid_monitor.cpp
@@ -71,8 +71,7 @@
!ParseUint(fields[8], &io[BACKGROUND].write_bytes) ||
!ParseUint(fields[9], &io[FOREGROUND].fsync) ||
!ParseUint(fields[10], &io[BACKGROUND].fsync)) {
- LOG_TO(SYSTEM, WARNING) << "Invalid uid I/O stats: \""
- << s << "\"";
+ LOG(WARNING) << "Invalid uid I/O stats: \"" << s << "\"";
return false;
}
return true;
@@ -95,8 +94,7 @@
!ParseUint(fields[size - 3], &io[BACKGROUND].write_bytes) ||
!ParseUint(fields[size - 2], &io[FOREGROUND].fsync) ||
!ParseUint(fields[size - 1], &io[BACKGROUND].fsync)) {
- LOG_TO(SYSTEM, WARNING) << "Invalid task I/O stats: \""
- << s << "\"";
+ LOG(WARNING) << "Invalid task I/O stats: \"" << s << "\"";
return false;
}
comm = Join(std::vector<std::string>(
@@ -123,13 +121,13 @@
{
sp<IServiceManager> sm = defaultServiceManager();
if (sm == NULL) {
- LOG_TO(SYSTEM, ERROR) << "defaultServiceManager failed";
+ LOG(ERROR) << "defaultServiceManager failed";
return;
}
sp<IBinder> binder = sm->getService(String16("package_native"));
if (binder == NULL) {
- LOG_TO(SYSTEM, ERROR) << "getService package_native failed";
+ LOG(ERROR) << "getService package_native failed";
return;
}
@@ -137,8 +135,7 @@
std::vector<std::string> names;
binder::Status status = package_mgr->getNamesForUids(uids, &names);
if (!status.isOk()) {
- LOG_TO(SYSTEM, ERROR) << "package_native::getNamesForUids failed: "
- << status.exceptionMessage();
+ LOG(ERROR) << "package_native::getNamesForUids failed: " << status.exceptionMessage();
return;
}
@@ -158,7 +155,7 @@
std::unordered_map<uint32_t, uid_info> uid_io_stats;
std::string buffer;
if (!ReadFileToString(UID_IO_STATS_PATH, &buffer)) {
- PLOG_TO(SYSTEM, ERROR) << UID_IO_STATS_PATH << ": ReadFileToString failed";
+ PLOG(ERROR) << UID_IO_STATS_PATH << ": ReadFileToString failed";
return uid_io_stats;
}
diff --git a/trusty/Android.bp b/trusty/Android.bp
deleted file mode 100644
index 2fb2e19..0000000
--- a/trusty/Android.bp
+++ /dev/null
@@ -1,6 +0,0 @@
-subdirs = [
- "gatekeeper",
- "keymaster",
- "libtrusty",
- "storage/*",
-]
diff --git a/trusty/confirmationui/.clang-format b/trusty/confirmationui/.clang-format
new file mode 100644
index 0000000..b0dc94c
--- /dev/null
+++ b/trusty/confirmationui/.clang-format
@@ -0,0 +1,10 @@
+BasedOnStyle: LLVM
+IndentWidth: 4
+UseTab: Never
+BreakBeforeBraces: Attach
+AllowShortFunctionsOnASingleLine: Inline
+AllowShortIfStatementsOnASingleLine: true
+IndentCaseLabels: false
+ColumnLimit: 100
+PointerBindsToType: true
+SpacesBeforeTrailingComments: 2
diff --git a/trusty/confirmationui/Android.bp b/trusty/confirmationui/Android.bp
new file mode 100644
index 0000000..60e0e71
--- /dev/null
+++ b/trusty/confirmationui/Android.bp
@@ -0,0 +1,95 @@
+// Copyright (C) 2020 The Android Open-Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// WARNING: Everything listed here will be built on ALL platforms,
+// including x86, the emulator, and the SDK. Modules must be uniquely
+// named (liblights.panda), and must build everywhere, or limit themselves
+// to only building on ARM if they include assembly. Individual makefiles
+// are responsible for having their own logic, for fine-grained control.
+
+cc_binary {
+ name: "android.hardware.confirmationui@1.0-service.trusty",
+ relative_install_path: "hw",
+ vendor: true,
+ shared_libs: [
+ "android.hardware.confirmationui@1.0",
+ "android.hardware.confirmationui.not-so-secure-input",
+ "android.hardware.confirmationui@1.0-lib.trusty",
+ "libbase",
+ "libhidlbase",
+ "libutils",
+ ],
+
+ init_rc: ["android.hardware.confirmationui@1.0-service.trusty.rc"],
+
+ vintf_fragments: ["android.hardware.confirmationui@1.0-service.trusty.xml"],
+
+ srcs: [
+ "service.cpp",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-DTEEUI_USE_STD_VECTOR",
+ ],
+}
+
+cc_library {
+ name: "android.hardware.confirmationui@1.0-lib.trusty",
+ vendor: true,
+ shared_libs: [
+ "android.hardware.confirmationui@1.0",
+ "android.hardware.keymaster@4.0",
+ "libbase",
+ "libhidlbase",
+ "libteeui_hal_support",
+ "libtrusty",
+ "libutils",
+ ],
+
+ export_include_dirs: ["include"],
+
+ srcs: [
+ "TrustyApp.cpp",
+ "TrustyConfirmationUI.cpp",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-DTEEUI_USE_STD_VECTOR",
+ ],
+}
+
+cc_library {
+ name: "android.hardware.confirmationui.not-so-secure-input",
+ vendor: true,
+ shared_libs: [
+ "libbase",
+ "libcrypto",
+ "libteeui_hal_support",
+ ],
+
+ srcs: [
+ "NotSoSecureInput.cpp",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-DTEEUI_USE_STD_VECTOR",
+ ],
+}
\ No newline at end of file
diff --git a/trusty/confirmationui/NotSoSecureInput.cpp b/trusty/confirmationui/NotSoSecureInput.cpp
new file mode 100644
index 0000000..3d9a2d6
--- /dev/null
+++ b/trusty/confirmationui/NotSoSecureInput.cpp
@@ -0,0 +1,207 @@
+/*
+ * 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 <android-base/logging.h>
+#include <endian.h>
+#include <memory>
+#include <openssl/hmac.h>
+#include <openssl/rand.h>
+#include <openssl/sha.h>
+#include <secure_input/evdev.h>
+#include <secure_input/secure_input_device.h>
+#include <teeui/utils.h>
+
+#include <initializer_list>
+
+using namespace secure_input;
+
+using teeui::AuthTokenKey;
+using teeui::ByteBufferProxy;
+using teeui::Hmac;
+using teeui::optional;
+using teeui::ResponseCode;
+using teeui::TestKeyBits;
+
+constexpr const auto kTestKey = AuthTokenKey::fill(static_cast<uint8_t>(TestKeyBits::BYTE));
+
+class SecureInputHMacer {
+ public:
+ static optional<Hmac> hmac256(const AuthTokenKey& key,
+ std::initializer_list<ByteBufferProxy> buffers) {
+ HMAC_CTX hmacCtx;
+ HMAC_CTX_init(&hmacCtx);
+ if (!HMAC_Init_ex(&hmacCtx, key.data(), key.size(), EVP_sha256(), nullptr)) {
+ return {};
+ }
+ for (auto& buffer : buffers) {
+ if (!HMAC_Update(&hmacCtx, buffer.data(), buffer.size())) {
+ return {};
+ }
+ }
+ Hmac result;
+ if (!HMAC_Final(&hmacCtx, result.data(), nullptr)) {
+ return {};
+ }
+ return result;
+ }
+};
+
+using HMac = teeui::HMac<SecureInputHMacer>;
+
+Nonce generateNonce() {
+ /*
+ * Completely random nonce.
+ * Running the secure input protocol from the HAL service is not secure
+ * because we don't trust the non-secure world (i.e., HLOS/Android/Linux). So
+ * using a constant "nonce" here does not weaken security. If this code runs
+ * on a truly trustworthy source of input events this function needs to return
+ * hight entropy nonces.
+ * As of this writing the call to RAND_bytes is commented, because the
+ * emulator this HAL service runs on does not have a good source of entropy.
+ * It would block the call to RAND_bytes indefinitely.
+ */
+ Nonce result{0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04};
+ // RAND_bytes(result.data(), result.size());
+ return result;
+}
+
+/**
+ * This is an implementation of the SecureInput protocol in unserspace. This is
+ * just an example and should not be used as is. The protocol implemented her
+ * should be used by a trusted input device that can assert user events with
+ * high assurance even if the HLOS kernel is compromised. A confirmationui HAL
+ * that links directly against this implementation is not secure and shal not be
+ * used on a production device.
+ */
+class NotSoSecureInput : public SecureInput {
+ public:
+ NotSoSecureInput(HsBeginCb hsBeginCb, HsFinalizeCb hsFinalizeCb, DeliverEventCb deliverEventCb,
+ InputResultCb inputResultCb)
+ : hsBeginCb_{hsBeginCb}, hsFinalizeCb_{hsFinalizeCb}, deliverEventCb_{deliverEventCb},
+ inputResultCb_{inputResultCb}, discardEvents_{true} {}
+
+ operator bool() const override { return true; }
+
+ void handleEvent(const EventDev& evdev) override {
+ bool gotEvent;
+ input_event evt;
+ std::tie(gotEvent, evt) = evdev.readEvent();
+ while (gotEvent) {
+ if (!(discardEvents_) && evt.type == EV_KEY &&
+ (evt.code == KEY_POWER || evt.code == KEY_VOLUMEDOWN || evt.code == KEY_VOLUMEUP) &&
+ evt.value == 1) {
+ DTupKeyEvent event = DTupKeyEvent::RESERVED;
+
+ // Translate the event code into DTupKeyEvent which the TA understands.
+ switch (evt.code) {
+ case KEY_POWER:
+ event = DTupKeyEvent::PWR;
+ break;
+ case KEY_VOLUMEDOWN:
+ event = DTupKeyEvent::VOL_DOWN;
+ break;
+ case KEY_VOLUMEUP:
+ event = DTupKeyEvent::VOL_UP;
+ break;
+ }
+
+ // The event goes into the HMAC in network byte order.
+ uint32_t keyEventBE = htobe32(static_cast<uint32_t>(event));
+ auto signature = HMac::hmac256(kTestKey, kConfirmationUIEventLabel,
+ teeui::bytesCast(keyEventBE), nCi_);
+
+ teeui::ResponseCode rc;
+ InputResponse ir;
+ auto response = std::tie(rc, ir);
+ if (event != DTupKeyEvent::RESERVED) {
+ response = deliverEventCb_(event, *signature);
+ if (rc != ResponseCode::OK) {
+ LOG(ERROR) << "DeliverInputEvent returned with " << uint32_t(rc);
+ inputResultCb_(rc);
+ } else {
+ switch (ir) {
+ case InputResponse::OK:
+ inputResultCb_(rc);
+ break;
+ case InputResponse::PENDING_MORE:
+ rc = performDTUPHandshake();
+ if (rc != ResponseCode::OK) {
+ inputResultCb_(rc);
+ }
+ break;
+ case InputResponse::TIMED_OUT:
+ inputResultCb_(rc);
+ break;
+ }
+ }
+ }
+ }
+ std::tie(gotEvent, evt) = evdev.readEvent();
+ }
+ }
+
+ void start() override {
+ auto rc = performDTUPHandshake();
+ if (rc != ResponseCode::OK) {
+ inputResultCb_(rc);
+ }
+ discardEvents_ = false;
+ };
+
+ private:
+ teeui::ResponseCode performDTUPHandshake() {
+ ResponseCode rc;
+ LOG(INFO) << "Start handshake";
+ Nonce nCo;
+ std::tie(rc, nCo) = hsBeginCb_();
+ if (rc != ResponseCode::OK) {
+ LOG(ERROR) << "Failed to begin secure input handshake (" << uint32_t(rc) << ")";
+ return rc;
+ }
+
+ nCi_ = generateNonce();
+ rc =
+ hsFinalizeCb_(*HMac::hmac256(kTestKey, kConfirmationUIHandshakeLabel, nCo, nCi_), nCi_);
+
+ if (rc != ResponseCode::OK) {
+ LOG(ERROR) << "Failed to finalize secure input handshake (" << uint32_t(rc) << ")";
+ return rc;
+ }
+ return ResponseCode::OK;
+ }
+
+ HsBeginCb hsBeginCb_;
+ HsFinalizeCb hsFinalizeCb_;
+ DeliverEventCb deliverEventCb_;
+ InputResultCb inputResultCb_;
+
+ std::atomic_bool discardEvents_;
+ Nonce nCi_;
+};
+
+namespace secure_input {
+
+std::shared_ptr<SecureInput> createSecureInput(SecureInput::HsBeginCb hsBeginCb,
+ SecureInput::HsFinalizeCb hsFinalizeCb,
+ SecureInput::DeliverEventCb deliverEventCb,
+ SecureInput::InputResultCb inputResultCb) {
+ return std::make_shared<NotSoSecureInput>(hsBeginCb, hsFinalizeCb, deliverEventCb,
+ inputResultCb);
+}
+
+} // namespace secure_input
diff --git a/trusty/confirmationui/README b/trusty/confirmationui/README
new file mode 100644
index 0000000..45d4e76
--- /dev/null
+++ b/trusty/confirmationui/README
@@ -0,0 +1,20 @@
+## Secure UI Architecture
+
+To implement confirmationui a secure UI architecture is required. This entails a way
+to display the confirmation dialog driven by a reduced trusted computing base, typically
+a trusted execution environment (TEE), without having to rely on Linux and the Android
+system for integrity and authenticity of input events. This implementation provides
+neither. But it provides most of the functionlity required to run a full Android Protected
+Confirmation feature when integrated into a secure UI architecture.
+
+## Secure input (NotSoSecureInput)
+
+This implementation does not provide any security guaranties.
+The input method (NotSoSecureInput) runs a cryptographic protocols that is
+sufficiently secure IFF the end point is implemented on a trustworthy
+secure input device. But since the endpoint is currently in the HAL
+service itself this implementation is not secure.
+
+NOTE that a secure input device end point needs a good source of entropy
+for generating nonces. The current implementation (NotSoSecureInput.cpp#generateNonce)
+uses a constant nonce.
\ No newline at end of file
diff --git a/trusty/confirmationui/TrustyApp.cpp b/trusty/confirmationui/TrustyApp.cpp
new file mode 100644
index 0000000..e4c68f9
--- /dev/null
+++ b/trusty/confirmationui/TrustyApp.cpp
@@ -0,0 +1,156 @@
+/*
+ * 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 "TrustyApp.h"
+
+#include <android-base/logging.h>
+#include <sys/uio.h>
+#include <trusty/tipc.h>
+
+namespace android {
+namespace trusty {
+
+// 0x1000 is the message buffer size but we need to leave some space for a protocol header.
+// This assures that packets can always be read/written in one read/write operation.
+static constexpr const uint32_t kPacketSize = 0x1000 - 32;
+
+enum class PacketType : uint32_t {
+ SND,
+ RCV,
+ ACK,
+};
+
+struct PacketHeader {
+ PacketType type;
+ uint32_t remaining;
+};
+
+const char* toString(PacketType t) {
+ switch (t) {
+ case PacketType::SND:
+ return "SND";
+ case PacketType::RCV:
+ return "RCV";
+ case PacketType::ACK:
+ return "ACK";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static constexpr const uint32_t kHeaderSize = sizeof(PacketHeader);
+static constexpr const uint32_t kPayloadSize = kPacketSize - kHeaderSize;
+
+ssize_t TrustyRpc(int handle, const uint8_t* obegin, const uint8_t* oend, uint8_t* ibegin,
+ uint8_t* iend) {
+ while (obegin != oend) {
+ PacketHeader header = {
+ .type = PacketType::SND,
+ .remaining = uint32_t(oend - obegin),
+ };
+ uint32_t body_size = std::min(kPayloadSize, header.remaining);
+ iovec iov[] = {
+ {
+ .iov_base = &header,
+ .iov_len = kHeaderSize,
+ },
+ {
+ .iov_base = const_cast<uint8_t*>(obegin),
+ .iov_len = body_size,
+ },
+ };
+ int rc = writev(handle, iov, 2);
+ if (!rc) {
+ PLOG(ERROR) << "Error sending SND message. " << rc;
+ return rc;
+ }
+
+ obegin += body_size;
+
+ rc = read(handle, &header, kHeaderSize);
+ if (!rc) {
+ PLOG(ERROR) << "Error reading ACK. " << rc;
+ return rc;
+ }
+
+ if (header.type != PacketType::ACK || header.remaining != oend - obegin) {
+ LOG(ERROR) << "malformed ACK";
+ return -1;
+ }
+ }
+
+ ssize_t remaining = 0;
+ auto begin = ibegin;
+ do {
+ PacketHeader header = {
+ .type = PacketType::RCV,
+ .remaining = 0,
+ };
+
+ iovec iov[] = {
+ {
+ .iov_base = &header,
+ .iov_len = kHeaderSize,
+ },
+ {
+ .iov_base = begin,
+ .iov_len = uint32_t(iend - begin),
+ },
+ };
+
+ ssize_t rc = writev(handle, iov, 1);
+ if (!rc) {
+ PLOG(ERROR) << "Error sending RCV message. " << rc;
+ return rc;
+ }
+
+ rc = readv(handle, iov, 2);
+ if (rc < 0) {
+ PLOG(ERROR) << "Error reading response. " << rc;
+ return rc;
+ }
+
+ uint32_t body_size = std::min(kPayloadSize, header.remaining);
+ if (body_size != rc - kHeaderSize) {
+ LOG(ERROR) << "Unexpected amount of data: " << rc;
+ return -1;
+ }
+
+ remaining = header.remaining - body_size;
+ begin += body_size;
+ } while (remaining);
+
+ return begin - ibegin;
+}
+
+TrustyApp::TrustyApp(const std::string& path, const std::string& appname)
+ : handle_(kInvalidHandle) {
+ handle_ = tipc_connect(path.c_str(), appname.c_str());
+ if (handle_ == kInvalidHandle) {
+ LOG(ERROR) << AT << "failed to connect to Trusty TA \"" << appname << "\" using dev:"
+ << "\"" << path << "\"";
+ }
+ LOG(INFO) << AT << "succeeded to connect to Trusty TA \"" << appname << "\"";
+}
+TrustyApp::~TrustyApp() {
+ if (handle_ != kInvalidHandle) {
+ tipc_close(handle_);
+ }
+ LOG(INFO) << "Done shutting down TrustyApp";
+}
+
+} // namespace trusty
+} // namespace android
diff --git a/trusty/confirmationui/TrustyApp.h b/trusty/confirmationui/TrustyApp.h
new file mode 100644
index 0000000..05a25f6
--- /dev/null
+++ b/trusty/confirmationui/TrustyApp.h
@@ -0,0 +1,155 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <android-base/logging.h>
+#include <errno.h>
+#include <poll.h>
+#include <stdio.h>
+#include <sys/eventfd.h>
+#include <sys/stat.h>
+#include <teeui/msg_formatting.h>
+#include <trusty/tipc.h>
+#include <unistd.h>
+
+#include <fstream>
+#include <functional>
+#include <future>
+#include <iostream>
+#include <sstream>
+#include <thread>
+#include <vector>
+
+#define AT __FILE__ ":" << __LINE__ << ": "
+
+namespace android {
+namespace trusty {
+
+using ::teeui::Message;
+using ::teeui::msg2tuple_t;
+using ::teeui::ReadStream;
+using ::teeui::WriteStream;
+
+#ifndef TEEUI_USE_STD_VECTOR
+/*
+ * TEEUI_USE_STD_VECTOR makes certain wire types like teeui::MsgString and
+ * teeui::MsgVector be aliases for std::vector. This is required for thread safe
+ * message serialization. Always compile this with -DTEEUI_USE_STD_VECTOR set in
+ * CFLAGS of the HAL service.
+ */
+#error "Must be compiled with -DTEEUI_USE_STD_VECTOR."
+#endif
+
+enum class TrustyAppError : int32_t {
+ OK,
+ ERROR = -1,
+ MSG_TOO_LONG = -2,
+};
+
+/*
+ * There is a hard limitation of 0x1800 bytes for the to-be-signed message size. The protocol
+ * overhead is limited, so that 0x2000 is a buffer size that will be sufficient in any benign
+ * mode of operation.
+ */
+static constexpr const size_t kSendBufferSize = 0x2000;
+
+ssize_t TrustyRpc(int handle, const uint8_t* obegin, const uint8_t* oend, uint8_t* ibegin,
+ uint8_t* iend);
+
+class TrustyApp {
+ private:
+ int handle_;
+ static constexpr const int kInvalidHandle = -1;
+ /*
+ * This mutex serializes communication with the trusted app, not handle_.
+ * Calling issueCmd during construction or deletion is undefined behavior.
+ */
+ std::mutex mutex_;
+
+ public:
+ TrustyApp(const std::string& path, const std::string& appname);
+ ~TrustyApp();
+
+ template <typename Request, typename Response, typename... T>
+ std::tuple<TrustyAppError, msg2tuple_t<Response>> issueCmd(const T&... args) {
+ std::lock_guard<std::mutex> lock(mutex_);
+
+ if (handle_ == kInvalidHandle) {
+ LOG(ERROR) << "TrustyApp not connected";
+ return {TrustyAppError::ERROR, {}};
+ }
+
+ uint8_t buffer[kSendBufferSize];
+ WriteStream out(buffer);
+
+ out = write(Request(), out, args...);
+ if (!out) {
+ LOG(ERROR) << AT << "send command failed: message formatting";
+ return {TrustyAppError::MSG_TOO_LONG, {}};
+ }
+
+ auto rc = TrustyRpc(handle_, &buffer[0], const_cast<const uint8_t*>(out.pos()), &buffer[0],
+ &buffer[kSendBufferSize]);
+ if (rc < 0) return {TrustyAppError::ERROR, {}};
+
+ ReadStream in(&buffer[0], rc);
+ auto result = read(Response(), in);
+ if (!std::get<0>(result)) {
+ LOG(ERROR) << "send command failed: message parsing";
+ return {TrustyAppError::ERROR, {}};
+ }
+
+ return {std::get<0>(result) ? TrustyAppError::OK : TrustyAppError::ERROR,
+ tuple_tail(std::move(result))};
+ }
+
+ template <typename Request, typename... T> TrustyAppError issueCmd(const T&... args) {
+ std::lock_guard<std::mutex> lock(mutex_);
+
+ if (handle_ == kInvalidHandle) {
+ LOG(ERROR) << "TrustyApp not connected";
+ return TrustyAppError::ERROR;
+ }
+
+ uint8_t buffer[kSendBufferSize];
+ WriteStream out(buffer);
+
+ out = write(Request(), out, args...);
+ if (!out) {
+ LOG(ERROR) << AT << "send command failed: message formatting";
+ return TrustyAppError::MSG_TOO_LONG;
+ }
+
+ auto rc = TrustyRpc(handle_, &buffer[0], const_cast<const uint8_t*>(out.pos()), &buffer[0],
+ &buffer[kSendBufferSize]);
+ if (rc < 0) {
+ LOG(ERROR) << "send command failed: " << strerror(errno) << " (" << errno << ")";
+ return TrustyAppError::ERROR;
+ }
+
+ if (rc > 0) {
+ LOG(ERROR) << "Unexpected non zero length response";
+ return TrustyAppError::ERROR;
+ }
+ return TrustyAppError::OK;
+ }
+
+ operator bool() const { return handle_ != kInvalidHandle; }
+};
+
+} // namespace trusty
+} // namespace android
diff --git a/trusty/confirmationui/TrustyConfirmationUI.cpp b/trusty/confirmationui/TrustyConfirmationUI.cpp
new file mode 100644
index 0000000..6b25893
--- /dev/null
+++ b/trusty/confirmationui/TrustyConfirmationUI.cpp
@@ -0,0 +1,513 @@
+/*
+ *
+ * Copyright 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 "TrustyConfirmationUI.h"
+
+#include <android-base/logging.h>
+#include <android/hardware/confirmationui/1.0/types.h>
+#include <android/hardware/keymaster/4.0/types.h>
+#include <fcntl.h>
+#include <linux/input.h>
+#include <poll.h>
+#include <pthread.h>
+#include <secure_input/evdev.h>
+#include <secure_input/secure_input_device.h>
+#include <secure_input/secure_input_proto.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <teeui/msg_formatting.h>
+#include <teeui/utils.h>
+#include <time.h>
+
+#include <atomic>
+#include <functional>
+#include <memory>
+#include <thread>
+#include <tuple>
+#include <vector>
+
+namespace android {
+namespace hardware {
+namespace confirmationui {
+namespace V1_0 {
+namespace implementation {
+
+using namespace secure_input;
+
+using ::android::trusty::TrustyAppError;
+
+using ::teeui::AbortMsg;
+using ::teeui::DeliverTestCommandMessage;
+using ::teeui::DeliverTestCommandResponse;
+using ::teeui::FetchConfirmationResult;
+using ::teeui::MsgString;
+using ::teeui::MsgVector;
+using ::teeui::PromptUserConfirmationMsg;
+using ::teeui::PromptUserConfirmationResponse;
+using ::teeui::ResultMsg;
+
+using ::secure_input::createSecureInput;
+
+using ::android::hardware::keymaster::V4_0::HardwareAuthToken;
+
+using ::std::tie;
+
+using TeeuiRc = ::teeui::ResponseCode;
+
+constexpr const char kTrustyDeviceName[] = "/dev/trusty-ipc-dev0";
+constexpr const char kConfirmationuiAppName[] = "com.android.trusty.confirmationui";
+
+namespace {
+
+class Finalize {
+ private:
+ std::function<void()> f_;
+
+ public:
+ Finalize(std::function<void()> f) : f_(f) {}
+ ~Finalize() {
+ if (f_) f_();
+ }
+ void release() { f_ = {}; }
+};
+
+ResponseCode convertRc(TeeuiRc trc) {
+ static_assert(
+ uint32_t(TeeuiRc::OK) == uint32_t(ResponseCode::OK) &&
+ uint32_t(TeeuiRc::Canceled) == uint32_t(ResponseCode::Canceled) &&
+ uint32_t(TeeuiRc::Aborted) == uint32_t(ResponseCode::Aborted) &&
+ uint32_t(TeeuiRc::OperationPending) == uint32_t(ResponseCode::OperationPending) &&
+ uint32_t(TeeuiRc::Ignored) == uint32_t(ResponseCode::Ignored) &&
+ uint32_t(TeeuiRc::SystemError) == uint32_t(ResponseCode::SystemError) &&
+ uint32_t(TeeuiRc::Unimplemented) == uint32_t(ResponseCode::Unimplemented) &&
+ uint32_t(TeeuiRc::Unexpected) == uint32_t(ResponseCode::Unexpected) &&
+ uint32_t(TeeuiRc::UIError) == uint32_t(ResponseCode::UIError) &&
+ uint32_t(TeeuiRc::UIErrorMissingGlyph) == uint32_t(ResponseCode::UIErrorMissingGlyph) &&
+ uint32_t(TeeuiRc::UIErrorMessageTooLong) ==
+ uint32_t(ResponseCode::UIErrorMessageTooLong) &&
+ uint32_t(TeeuiRc::UIErrorMalformedUTF8Encoding) ==
+ uint32_t(ResponseCode::UIErrorMalformedUTF8Encoding),
+ "teeui::ResponseCode and "
+ "::android::hardware::confirmationui::V1_0::Responsecude are out of "
+ "sync");
+ return ResponseCode(trc);
+}
+
+teeui::UIOption convertUIOption(UIOption uio) {
+ static_assert(uint32_t(UIOption::AccessibilityInverted) ==
+ uint32_t(teeui::UIOption::AccessibilityInverted) &&
+ uint32_t(UIOption::AccessibilityMagnified) ==
+ uint32_t(teeui::UIOption::AccessibilityMagnified),
+ "teeui::UIOPtion and ::android::hardware::confirmationui::V1_0::UIOption "
+ "anre out of sync");
+ return teeui::UIOption(uio);
+}
+
+inline MsgString hidl2MsgString(const hidl_string& s) {
+ return {s.c_str(), s.c_str() + s.size()};
+}
+template <typename T> inline MsgVector<T> hidl2MsgVector(const hidl_vec<T>& v) {
+ return {v};
+}
+
+inline MsgVector<teeui::UIOption> hidl2MsgVector(const hidl_vec<UIOption>& v) {
+ MsgVector<teeui::UIOption> result(v.size());
+ for (unsigned int i = 0; i < v.size(); ++i) {
+ result[i] = convertUIOption(v[i]);
+ }
+ return result;
+}
+
+} // namespace
+
+TrustyConfirmationUI::TrustyConfirmationUI()
+ : listener_state_(ListenerState::None), prompt_result_(ResponseCode::Ignored) {}
+
+TrustyConfirmationUI::~TrustyConfirmationUI() {
+ ListenerState state = listener_state_;
+ if (state == ListenerState::SetupDone || state == ListenerState::Interactive) {
+ abort();
+ }
+ if (state != ListenerState::None) {
+ callback_thread_.join();
+ }
+}
+
+std::tuple<TeeuiRc, MsgVector<uint8_t>, MsgVector<uint8_t>>
+TrustyConfirmationUI::promptUserConfirmation_(const MsgString& promptText,
+ const MsgVector<uint8_t>& extraData,
+ const MsgString& locale,
+ const MsgVector<teeui::UIOption>& uiOptions) {
+ std::unique_lock<std::mutex> stateLock(listener_state_lock_);
+ /*
+ * This is the main listener thread function. The listener thread life cycle
+ * is equivalent to the life cycle of a single confirmation request. The life
+ * cycle is devided in four phases.
+ * * The starting phase:
+ * * The Trusted App gets loaded and/or the connection to it gets established.
+ * * A connection to the secure input device is established.
+ * * The prompt is initiated. This sends all information required by the
+ * confirmation dialog to the TA. The dialog is not yet displayed.
+ * * An event loop is created.
+ * * The event loop listens for user input events, fetches them from the
+ * secure input device, and delivers them to the TA.
+ * * All evdev devices are grabbed to give confirmationui exclusive access
+ * to user input.
+ *
+ * Note: During the starting phase the hwbinder service thread is blocked and
+ * waiting for possible Errors. If the setup phase concludes sucessfully, the
+ * hwbinder service thread gets unblocked and returns successfully. Errors
+ * that occur after the first phase are delivered by callback interface.
+ *
+ * * The 2nd phase - non interactive phase
+ * * The event loop thread is started.
+ * * After a grace period:
+ * * A handshake between the secure input device SecureInput and the TA
+ * is performed.
+ * * The input event handler are armed to process user input events.
+ *
+ * * The 3rd phase - interactive phase
+ * * We wait to any external event
+ * * Abort
+ * * Secure user input asserted
+ * * Secure input delivered (for non interactive VTS testing)
+ * * The result is fetched from the TA.
+ *
+ * * The 4th phase - cleanup
+ * The cleanup phase is given by the scope of automatic variables created
+ * in this function. The cleanup commences in reverse order of their creation.
+ * Here is a list of more complex items in the order in which they go out of
+ * scope
+ * * finalizeSecureTouch - signals and joins the secure touch thread.
+ * * eventloop - signals and joins the event loop thread. The event
+ * handlers also own all EventDev instances which ungrab the event devices.
+ * When the eventloop goes out of scope the EventDevs get destroyed
+ * relinquishing the exclusive hold on the event devices.
+ * * finalizeConfirmationPrompt - calls abort on the TA, making sure a
+ * pending operation gets canceled. If the prompt concluded successfully this
+ * is a spurious call but semantically a no op.
+ * * secureInput - shuts down the connection to the secure input device
+ * SecureInput.
+ * * app - disconnects the TA. Since app is a shared pointer this may not
+ * unload the app here. It is possible that more instances of the shared
+ * pointer are held in TrustyConfirmationUI::deliverSecureInputEvent and
+ * TrustyConfirmationUI::abort. But these instances are extremely short lived
+ * and it is safe if they are destroyed by either.
+ * * stateLock - unlocks the listener_state_lock_ if it happens to be held
+ * at the time of return.
+ */
+
+ std::tuple<TeeuiRc, MsgVector<uint8_t>, MsgVector<uint8_t>> result;
+ TeeuiRc& rc = std::get<TeeuiRc>(result);
+ rc = TeeuiRc::SystemError;
+
+ listener_state_ = ListenerState::Starting;
+
+ auto app = std::make_shared<TrustyApp>(kTrustyDeviceName, kConfirmationuiAppName);
+ if (!app) return result; // TeeuiRc::SystemError
+
+ app_ = app;
+
+ auto hsBegin = [&]() -> std::tuple<TeeuiRc, Nonce> {
+ auto [error, result] =
+ app->issueCmd<secure_input::InputHandshake, secure_input::InputHandshakeResponse>();
+ auto& [rc, nCo] = result;
+
+ if (error != TrustyAppError::OK || rc != TeeuiRc::OK) {
+ LOG(ERROR) << "Failed to begin secure input handshake (" << int32_t(error) << "/"
+ << uint32_t(rc) << ")";
+ rc = error != TrustyAppError::OK ? TeeuiRc::SystemError : rc;
+ }
+ return result;
+ };
+
+ auto hsFinalize = [&](const Signature& sig, const Nonce& nCi) -> TeeuiRc {
+ auto [error, finalizeResponse] =
+ app->issueCmd<FinalizeInputSessionHandshake, FinalizeInputSessionHandshakeResponse>(
+ nCi, sig);
+ auto& [rc] = finalizeResponse;
+ if (error != TrustyAppError::OK || rc != TeeuiRc::OK) {
+ LOG(ERROR) << "Failed to finalize secure input handshake (" << int32_t(error) << "/"
+ << uint32_t(rc) << ")";
+ rc = error != TrustyAppError::OK ? TeeuiRc::SystemError : rc;
+ }
+ return rc;
+ };
+
+ auto deliverInput = [&](DTupKeyEvent event,
+ const Signature& sig) -> std::tuple<TeeuiRc, InputResponse> {
+ auto [error, result] =
+ app->issueCmd<DeliverInputEvent, DeliverInputEventResponse>(event, sig);
+ auto& [rc, ir] = result;
+ if (error != TrustyAppError::OK) {
+ LOG(ERROR) << "Failed to deliver input command";
+ rc = TeeuiRc::SystemError;
+ }
+ return result;
+ };
+
+ std::atomic<TeeuiRc> eventRC = TeeuiRc::OperationPending;
+ auto inputResult = [&](TeeuiRc rc) {
+ TeeuiRc expected = TeeuiRc::OperationPending;
+ if (eventRC.compare_exchange_strong(expected, rc)) {
+ listener_state_condv_.notify_all();
+ }
+ };
+
+ // create Secure Input device.
+ auto secureInput = createSecureInput(hsBegin, hsFinalize, deliverInput, inputResult);
+ if (!secureInput || !(*secureInput)) {
+ LOG(ERROR) << "Failed to open secure input device";
+ return result; // TeeuiRc::SystemError;
+ }
+
+ Finalize finalizeConfirmationPrompt([app] {
+ LOG(INFO) << "Calling abort for cleanup";
+ app->issueCmd<AbortMsg>();
+ });
+
+ // initiate prompt
+ LOG(INFO) << "Initiating prompt";
+ TrustyAppError error;
+ auto initResponse = std::tie(rc);
+ std::tie(error, initResponse) =
+ app->issueCmd<PromptUserConfirmationMsg, PromptUserConfirmationResponse>(
+ promptText, extraData, locale, uiOptions);
+ if (error == TrustyAppError::MSG_TOO_LONG) {
+ LOG(ERROR) << "PromptUserConfirmationMsg failed: message too long";
+ rc = TeeuiRc::UIErrorMessageTooLong;
+ return result;
+ } else if (error != TrustyAppError::OK) {
+ LOG(ERROR) << "PromptUserConfirmationMsg failed: " << int32_t(error);
+ return result; // TeeuiRc::SystemError;
+ }
+ if (rc != TeeuiRc::OK) {
+ LOG(ERROR) << "PromptUserConfirmationMsg failed: " << uint32_t(rc);
+ return result;
+ }
+
+ LOG(INFO) << "Grabbing event devices";
+ EventLoop eventloop;
+ bool grabbed =
+ grabAllEvDevsAndRegisterCallbacks(&eventloop, [&](short flags, const EventDev& evDev) {
+ if (!(flags & POLLIN)) return;
+ secureInput->handleEvent(evDev);
+ });
+
+ if (!grabbed) {
+ rc = TeeuiRc::SystemError;
+ return result;
+ }
+
+ abort_called_ = false;
+ secureInputDelivered_ = false;
+
+ // ############################## Start 2nd Phase #############################################
+ listener_state_ = ListenerState::SetupDone;
+ stateLock.unlock();
+ listener_state_condv_.notify_all();
+
+ if (!eventloop.start()) {
+ rc = TeeuiRc::SystemError;
+ return result;
+ }
+
+ stateLock.lock();
+
+ LOG(INFO) << "going to sleep for the grace period";
+ auto then = std::chrono::system_clock::now() +
+ std::chrono::milliseconds(kUserPreInputGracePeriodMillis) +
+ std::chrono::microseconds(50);
+ listener_state_condv_.wait_until(stateLock, then, [&]() { return abort_called_; });
+ LOG(INFO) << "waking up";
+
+ if (abort_called_) {
+ LOG(ERROR) << "Abort called";
+ result = {TeeuiRc::Aborted, {}, {}};
+ return result;
+ }
+
+ LOG(INFO) << "Arming event poller";
+ // tell the event poller to act on received input events from now on.
+ secureInput->start();
+
+ // ############################## Start 3rd Phase - interactive phase #########################
+ LOG(INFO) << "Transition to Interactive";
+ listener_state_ = ListenerState::Interactive;
+ stateLock.unlock();
+ listener_state_condv_.notify_all();
+
+ stateLock.lock();
+ listener_state_condv_.wait(stateLock, [&]() {
+ return eventRC != TeeuiRc::OperationPending || abort_called_ || secureInputDelivered_;
+ });
+ LOG(INFO) << "Listener waking up";
+ if (abort_called_) {
+ LOG(ERROR) << "Abort called";
+ result = {TeeuiRc::Aborted, {}, {}};
+ return result;
+ }
+
+ if (!secureInputDelivered_) {
+ if (eventRC != TeeuiRc::OK) {
+ LOG(ERROR) << "Bad input response";
+ result = {eventRC, {}, {}};
+ return result;
+ }
+ }
+
+ stateLock.unlock();
+
+ LOG(INFO) << "Fetching Result";
+ std::tie(error, result) = app->issueCmd<FetchConfirmationResult, ResultMsg>();
+ LOG(INFO) << "Result yields " << int32_t(error) << "/" << uint32_t(rc);
+ if (error != TrustyAppError::OK) {
+ result = {TeeuiRc::SystemError, {}, {}};
+ }
+ return result;
+
+ // ############################## Start 4th Phase - cleanup ##################################
+}
+
+// Methods from ::android::hardware::confirmationui::V1_0::IConfirmationUI
+// follow.
+Return<ResponseCode> TrustyConfirmationUI::promptUserConfirmation(
+ const sp<IConfirmationResultCallback>& resultCB, const hidl_string& promptText,
+ const hidl_vec<uint8_t>& extraData, const hidl_string& locale,
+ const hidl_vec<UIOption>& uiOptions) {
+ std::unique_lock<std::mutex> stateLock(listener_state_lock_, std::defer_lock);
+ if (!stateLock.try_lock()) {
+ return ResponseCode::OperationPending;
+ }
+ switch (listener_state_) {
+ case ListenerState::None:
+ break;
+ case ListenerState::Starting:
+ case ListenerState::SetupDone:
+ case ListenerState::Interactive:
+ return ResponseCode::OperationPending;
+ case ListenerState::Terminating:
+ callback_thread_.join();
+ listener_state_ = ListenerState::None;
+ break;
+ default:
+ return ResponseCode::Unexpected;
+ }
+
+ assert(listener_state_ == ListenerState::None);
+
+ callback_thread_ = std::thread(
+ [this](sp<IConfirmationResultCallback> resultCB, hidl_string promptText,
+ hidl_vec<uint8_t> extraData, hidl_string locale, hidl_vec<UIOption> uiOptions) {
+ auto [trc, msg, token] =
+ promptUserConfirmation_(hidl2MsgString(promptText), hidl2MsgVector(extraData),
+ hidl2MsgString(locale), hidl2MsgVector(uiOptions));
+ bool do_callback = (listener_state_ == ListenerState::Interactive ||
+ listener_state_ == ListenerState::SetupDone) &&
+ resultCB;
+ prompt_result_ = convertRc(trc);
+ listener_state_ = ListenerState::Terminating;
+ if (do_callback) {
+ auto error = resultCB->result(prompt_result_, msg, token);
+ if (!error.isOk()) {
+ LOG(ERROR) << "Result callback failed " << error.description();
+ }
+ } else {
+ listener_state_condv_.notify_all();
+ }
+ },
+ resultCB, promptText, extraData, locale, uiOptions);
+
+ listener_state_condv_.wait(stateLock, [this] {
+ return listener_state_ == ListenerState::SetupDone ||
+ listener_state_ == ListenerState::Interactive ||
+ listener_state_ == ListenerState::Terminating;
+ });
+ if (listener_state_ == ListenerState::Terminating) {
+ callback_thread_.join();
+ listener_state_ = ListenerState::None;
+ return prompt_result_;
+ }
+ return ResponseCode::OK;
+}
+
+Return<ResponseCode>
+TrustyConfirmationUI::deliverSecureInputEvent(const HardwareAuthToken& secureInputToken) {
+ ResponseCode rc = ResponseCode::Ignored;
+ {
+ /*
+ * deliverSecureInputEvent is only used by the VTS test to mock human input. A correct
+ * implementation responds with a mock confirmation token signed with a test key. The
+ * problem is that the non interactive grace period was not formalized in the HAL spec,
+ * so that the VTS test does not account for the grace period. (It probably should.)
+ * This means we can only pass the VTS test if we block until the grace period is over
+ * (SetupDone -> Interactive) before we deliver the input event.
+ *
+ * The true secure input is delivered by a different mechanism and gets ignored -
+ * not queued - until the grace period is over.
+ *
+ */
+ std::unique_lock<std::mutex> stateLock(listener_state_lock_);
+ listener_state_condv_.wait(stateLock,
+ [this] { return listener_state_ != ListenerState::SetupDone; });
+
+ if (listener_state_ != ListenerState::Interactive) return ResponseCode::Ignored;
+ auto sapp = app_.lock();
+ if (!sapp) return ResponseCode::Ignored;
+ auto [error, response] =
+ sapp->issueCmd<DeliverTestCommandMessage, DeliverTestCommandResponse>(
+ static_cast<teeui::TestModeCommands>(secureInputToken.challenge));
+ if (error != TrustyAppError::OK) return ResponseCode::SystemError;
+ auto& [trc] = response;
+ if (trc != TeeuiRc::Ignored) secureInputDelivered_ = true;
+ rc = convertRc(trc);
+ }
+ if (secureInputDelivered_) listener_state_condv_.notify_all();
+ // VTS test expect an OK response if the event was successfully delivered.
+ // But since the TA returns the callback response now, we have to translate
+ // Canceled into OK. Canceled is only returned if the delivered event canceled
+ // the operation, which means that the event was successfully delivered. Thus
+ // we return OK.
+ if (rc == ResponseCode::Canceled) return ResponseCode::OK;
+ return rc;
+}
+
+Return<void> TrustyConfirmationUI::abort() {
+ {
+ std::unique_lock<std::mutex> stateLock(listener_state_lock_);
+ if (listener_state_ == ListenerState::SetupDone ||
+ listener_state_ == ListenerState::Interactive) {
+ auto sapp = app_.lock();
+ if (sapp) sapp->issueCmd<AbortMsg>();
+ abort_called_ = true;
+ }
+ }
+ listener_state_condv_.notify_all();
+ return Void();
+}
+
+android::sp<IConfirmationUI> createTrustyConfirmationUI() {
+ return new TrustyConfirmationUI();
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace confirmationui
+} // namespace hardware
+} // namespace android
diff --git a/trusty/confirmationui/TrustyConfirmationUI.h b/trusty/confirmationui/TrustyConfirmationUI.h
new file mode 100644
index 0000000..3a7c7ef
--- /dev/null
+++ b/trusty/confirmationui/TrustyConfirmationUI.h
@@ -0,0 +1,104 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_CONFIRMATIONUI_V1_0_TRUSTY_CONFIRMATIONUI_H
+#define ANDROID_HARDWARE_CONFIRMATIONUI_V1_0_TRUSTY_CONFIRMATIONUI_H
+
+#include <android/hardware/confirmationui/1.0/IConfirmationUI.h>
+#include <android/hardware/keymaster/4.0/types.h>
+#include <hidl/Status.h>
+
+#include <atomic>
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+#include <teeui/generic_messages.h>
+#include <thread>
+
+#include "TrustyApp.h"
+
+namespace android {
+namespace hardware {
+namespace confirmationui {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::sp;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+
+using ::android::trusty::TrustyApp;
+
+class TrustyConfirmationUI : public IConfirmationUI {
+ public:
+ TrustyConfirmationUI();
+ virtual ~TrustyConfirmationUI();
+ // Methods from ::android::hardware::confirmationui::V1_0::IConfirmationUI
+ // follow.
+ Return<ResponseCode> promptUserConfirmation(const sp<IConfirmationResultCallback>& resultCB,
+ const hidl_string& promptText,
+ const hidl_vec<uint8_t>& extraData,
+ const hidl_string& locale,
+ const hidl_vec<UIOption>& uiOptions) override;
+ Return<ResponseCode> deliverSecureInputEvent(
+ const ::android::hardware::keymaster::V4_0::HardwareAuthToken& secureInputToken) override;
+ Return<void> abort() override;
+
+ private:
+ std::weak_ptr<TrustyApp> app_;
+ std::thread callback_thread_;
+
+ enum class ListenerState : uint32_t {
+ None,
+ Starting,
+ SetupDone,
+ Interactive,
+ Terminating,
+ };
+
+ /*
+ * listener_state is protected by listener_state_lock. It makes transitions between phases
+ * of the confirmation operation atomic.
+ * (See TrustyConfirmationUI.cpp#promptUserConfirmation_ for details about operation phases)
+ */
+ ListenerState listener_state_;
+ /*
+ * abort_called_ is also protected by listener_state_lock_ and indicates that the HAL user
+ * called abort.
+ */
+ bool abort_called_;
+ std::mutex listener_state_lock_;
+ std::condition_variable listener_state_condv_;
+ ResponseCode prompt_result_;
+ bool secureInputDelivered_;
+
+ std::tuple<teeui::ResponseCode, teeui::MsgVector<uint8_t>, teeui::MsgVector<uint8_t>>
+ promptUserConfirmation_(const teeui::MsgString& promptText,
+ const teeui::MsgVector<uint8_t>& extraData,
+ const teeui::MsgString& locale,
+ const teeui::MsgVector<teeui::UIOption>& uiOptions);
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace confirmationui
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_CONFIRMATIONUI_V1_0_TRUSTY_CONFIRMATIONUI_H
diff --git a/trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.rc b/trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.rc
new file mode 100644
index 0000000..dc7a03b
--- /dev/null
+++ b/trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.rc
@@ -0,0 +1,4 @@
+service confirmationui-1-0 /vendor/bin/hw/android.hardware.confirmationui@1.0-service.trusty
+ class hal
+ user nobody
+ group drmrpc input
diff --git a/trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.xml b/trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.xml
new file mode 100644
index 0000000..9008b87
--- /dev/null
+++ b/trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="device">
+ <hal format="hidl">
+ <name>android.hardware.confirmationui</name>
+ <transport>hwbinder</transport>
+ <version>1.0</version>
+ <interface>
+ <name>IConfirmationUI</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/trusty/confirmationui/include/TrustyConfirmationuiHal.h b/trusty/confirmationui/include/TrustyConfirmationuiHal.h
new file mode 100644
index 0000000..2ab9389
--- /dev/null
+++ b/trusty/confirmationui/include/TrustyConfirmationuiHal.h
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <android/hardware/confirmationui/1.0/IConfirmationUI.h>
+
+namespace android {
+namespace hardware {
+namespace confirmationui {
+namespace V1_0 {
+namespace implementation {
+
+android::sp<IConfirmationUI> createTrustyConfirmationUI();
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace confirmationui
+} // namespace hardware
+} // namespace android
diff --git a/trusty/confirmationui/service.cpp b/trusty/confirmationui/service.cpp
new file mode 100644
index 0000000..dd7e84b
--- /dev/null
+++ b/trusty/confirmationui/service.cpp
@@ -0,0 +1,35 @@
+/*
+ * 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 <android-base/logging.h>
+#include <hidl/HidlTransportSupport.h>
+
+#include <TrustyConfirmationuiHal.h>
+
+using android::sp;
+using android::hardware::confirmationui::V1_0::implementation::createTrustyConfirmationUI;
+
+int main() {
+ ::android::hardware::configureRpcThreadpool(1, true /*willJoinThreadpool*/);
+ auto service = createTrustyConfirmationUI();
+ auto status = service->registerAsService();
+ if (status != android::OK) {
+ LOG(FATAL) << "Could not register service for ConfirmationUI 1.0 (" << status << ")";
+ return -1;
+ }
+ ::android::hardware::joinRpcThreadpool();
+ return -1;
+}
diff --git a/trusty/libtrusty/Android.bp b/trusty/libtrusty/Android.bp
index f6e9bee..8dba78d 100644
--- a/trusty/libtrusty/Android.bp
+++ b/trusty/libtrusty/Android.bp
@@ -12,10 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-subdirs = [
- "tipc-test",
-]
-
cc_library {
name: "libtrusty",
vendor: true,