diff --git a/base/.gitignore b/base/.gitignore
new file mode 100644
index 000000000..42afabfd2
--- /dev/null
+++ b/base/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/base/build.gradle.kts b/base/build.gradle.kts
new file mode 100644
index 000000000..1801fd353
--- /dev/null
+++ b/base/build.gradle.kts
@@ -0,0 +1,32 @@
+plugins {
+ id("ndksamples.android.library")
+}
+
+android {
+ namespace = "com.android.ndk.samples.base"
+
+ defaultConfig {
+ externalNativeBuild {
+ cmake {
+ arguments.add("-DANDROID_WEAK_API_DEFS=ON")
+ }
+ }
+ }
+
+ externalNativeBuild {
+ cmake {
+ path = file("src/main/cpp/CMakeLists.txt")
+ }
+ }
+
+ buildFeatures {
+ prefab = true
+ prefabPublishing = true
+ }
+
+ prefab {
+ create("base") {
+ headers = "src/main/cpp/include"
+ }
+ }
+}
diff --git a/base/src/main/AndroidManifest.xml b/base/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..a5918e68a
--- /dev/null
+++ b/base/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/base/src/main/cpp/CMakeLists.txt b/base/src/main/cpp/CMakeLists.txt
new file mode 100644
index 000000000..c98d205d5
--- /dev/null
+++ b/base/src/main/cpp/CMakeLists.txt
@@ -0,0 +1,16 @@
+cmake_minimum_required(VERSION 3.22.1)
+project(base LANGUAGES CXX)
+
+add_compile_options(-Wall -Werror -Wextra)
+
+add_library(base
+ STATIC
+ logging.cpp
+ stringprintf.cpp
+ strings.cpp
+)
+
+target_include_directories(base
+ PUBLIC
+ include
+)
diff --git a/base/src/main/cpp/include/base/errno_restorer.h b/base/src/main/cpp/include/base/errno_restorer.h
new file mode 100644
index 000000000..ceb7f88d0
--- /dev/null
+++ b/base/src/main/cpp/include/base/errno_restorer.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include
+
+#include "base/macros.h"
+
+namespace android {
+namespace base {
+
+class ErrnoRestorer {
+ public:
+ ErrnoRestorer() : saved_errno_(errno) {}
+
+ ~ErrnoRestorer() { errno = saved_errno_; }
+
+ // Allow this object to be used as part of && operation.
+ explicit operator bool() const { return true; }
+
+ private:
+ const int saved_errno_;
+
+ DISALLOW_COPY_AND_ASSIGN(ErrnoRestorer);
+};
+
+} // namespace base
+} // namespace android
diff --git a/base/src/main/cpp/include/base/logging.h b/base/src/main/cpp/include/base/logging.h
new file mode 100644
index 000000000..51edbb557
--- /dev/null
+++ b/base/src/main/cpp/include/base/logging.h
@@ -0,0 +1,466 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+//
+// Google-style C++ logging.
+//
+
+// This header provides a C++ stream interface to logging.
+//
+// To log:
+//
+// LOG(INFO) << "Some text; " << some_value;
+//
+// Replace `INFO` with any severity from `enum LogSeverity`.
+// Most devices filter out VERBOSE logs by default, run
+// `adb shell setprop log.tag. V` to see them in adb logcat.
+//
+// To log the result of a failed function and include the string
+// representation of `errno` at the end:
+//
+// PLOG(ERROR) << "Write failed";
+//
+// The output will be something like `Write failed: I/O error`.
+// Remember this as 'P' as in perror(3).
+//
+// To output your own types, simply implement operator<< as normal.
+//
+// By default, output goes to logcat on Android and stderr on the host.
+// A process can use `SetLogger` to decide where all logging goes.
+// Implementations are provided for logcat, stderr, and dmesg.
+//
+// By default, the process' name is used as the log tag.
+// Code can choose a specific log tag by defining LOG_TAG
+// before including this header.
+
+// This header also provides assertions:
+//
+// CHECK(must_be_true);
+// CHECK_EQ(a, b) << z_is_interesting_too;
+
+
+#include
+#include
+#include
+
+#include "base/errno_restorer.h"
+#include "base/macros.h"
+
+// Note: DO NOT USE DIRECTLY. Use LOG_TAG instead.
+#ifdef _LOG_TAG_INTERNAL
+#error "_LOG_TAG_INTERNAL must not be defined"
+#endif
+#ifdef LOG_TAG
+#define _LOG_TAG_INTERNAL LOG_TAG
+#else
+#define _LOG_TAG_INTERNAL nullptr
+#endif
+
+namespace android {
+namespace base {
+
+enum LogSeverity {
+ VERBOSE,
+ DEBUG,
+ INFO,
+ WARNING,
+ ERROR,
+ FATAL_WITHOUT_ABORT, // For loggability tests, this is considered identical to FATAL.
+ FATAL,
+};
+
+enum LogId {
+ DEFAULT,
+ MAIN,
+ SYSTEM,
+ RADIO,
+ CRASH,
+};
+
+using LogFunction = std::function;
+using AbortFunction = std::function;
+
+// Loggers for use with InitLogging/SetLogger.
+
+LogFunction TeeLogger(LogFunction&& l1, LogFunction&& l2);
+
+void DefaultAborter(const char* abort_message);
+
+void SetDefaultTag(const std::string& tag);
+
+// The LogdLogger sends chunks of up to ~4000 bytes at a time to logd. It does not prevent other
+// threads from writing to logd between sending each chunk, so other threads may interleave their
+// messages. If preventing interleaving is required, then a custom logger that takes a lock before
+// calling this logger should be provided.
+class LogdLogger {
+ public:
+ explicit LogdLogger(LogId default_log_id = android::base::MAIN);
+
+ void operator()(LogId, LogSeverity, const char* tag, const char* file,
+ unsigned int line, const char* message);
+
+ private:
+ LogId default_log_id_;
+};
+
+// Configure logging based on ANDROID_LOG_TAGS environment variable.
+// We need to parse a string that looks like
+//
+// *:v jdwp:d dalvikvm:d dalvikvm-gc:i dalvikvmi:i
+//
+// The tag (or '*' for the global level) comes first, followed by a colon and a
+// letter indicating the minimum priority level we're expected to log. This can
+// be used to reveal or conceal logs with specific tags.
+#ifdef __ANDROID__
+#define INIT_LOGGING_DEFAULT_LOGGER LogdLogger()
+#else
+#define INIT_LOGGING_DEFAULT_LOGGER StderrLogger
+#endif
+void InitLogging(char* argv[],
+ LogFunction&& logger = INIT_LOGGING_DEFAULT_LOGGER,
+ AbortFunction&& aborter = DefaultAborter);
+#undef INIT_LOGGING_DEFAULT_LOGGER
+
+// Replace the current logger and return the old one.
+LogFunction SetLogger(LogFunction&& logger);
+
+// Replace the current aborter and return the old one.
+AbortFunction SetAborter(AbortFunction&& aborter);
+
+// A helper macro that produces an expression that accepts both a qualified name and an
+// unqualified name for a LogSeverity, and returns a LogSeverity value.
+// Note: DO NOT USE DIRECTLY. This is an implementation detail.
+#define SEVERITY_LAMBDA(severity) ([&]() { \
+ using ::android::base::VERBOSE; \
+ using ::android::base::DEBUG; \
+ using ::android::base::INFO; \
+ using ::android::base::WARNING; \
+ using ::android::base::ERROR; \
+ using ::android::base::FATAL_WITHOUT_ABORT; \
+ using ::android::base::FATAL; \
+ return (severity); }())
+
+#ifdef __clang_analyzer__
+// Clang's static analyzer does not see the conditional statement inside
+// LogMessage's destructor that will abort on FATAL severity.
+#define ABORT_AFTER_LOG_FATAL for (;; abort())
+
+struct LogAbortAfterFullExpr {
+ ~LogAbortAfterFullExpr() __attribute__((noreturn)) { abort(); }
+ explicit operator bool() const { return false; }
+};
+// Provides an expression that evaluates to the truthiness of `x`, automatically
+// aborting if `c` is true.
+#define ABORT_AFTER_LOG_EXPR_IF(c, x) (((c) && ::android::base::LogAbortAfterFullExpr()) || (x))
+// Note to the static analyzer that we always execute FATAL logs in practice.
+#define MUST_LOG_MESSAGE(severity) (SEVERITY_LAMBDA(severity) == ::android::base::FATAL)
+#else
+#define ABORT_AFTER_LOG_FATAL
+#define ABORT_AFTER_LOG_EXPR_IF(c, x) (x)
+#define MUST_LOG_MESSAGE(severity) false
+#endif
+#define ABORT_AFTER_LOG_FATAL_EXPR(x) ABORT_AFTER_LOG_EXPR_IF(true, x)
+
+// Defines whether the given severity will be logged or silently swallowed.
+#define WOULD_LOG(severity) \
+ (UNLIKELY(::android::base::ShouldLog(SEVERITY_LAMBDA(severity), _LOG_TAG_INTERNAL)) || \
+ MUST_LOG_MESSAGE(severity))
+
+// Get an ostream that can be used for logging at the given severity and to the default
+// destination.
+//
+// Notes:
+// 1) This will not check whether the severity is high enough. One should use WOULD_LOG to filter
+// usage manually.
+// 2) This does not save and restore errno.
+#define LOG_STREAM(severity) \
+ ::android::base::LogMessage(__FILE__, __LINE__, SEVERITY_LAMBDA(severity), _LOG_TAG_INTERNAL, \
+ -1) \
+ .stream()
+
+// Logs a message to logcat on Android otherwise to stderr. If the severity is
+// FATAL it also causes an abort. For example:
+//
+// LOG(FATAL) << "We didn't expect to reach here";
+#define LOG(severity) LOGGING_PREAMBLE(severity) && LOG_STREAM(severity)
+
+// Checks if we want to log something, and sets up appropriate RAII objects if
+// so.
+// Note: DO NOT USE DIRECTLY. This is an implementation detail.
+#define LOGGING_PREAMBLE(severity) \
+ (WOULD_LOG(severity) && \
+ ABORT_AFTER_LOG_EXPR_IF((SEVERITY_LAMBDA(severity)) == ::android::base::FATAL, true) && \
+ ::android::base::ErrnoRestorer())
+
+// A variant of LOG that also logs the current errno value. To be used when
+// library calls fail.
+#define PLOG(severity) \
+ LOGGING_PREAMBLE(severity) && \
+ ::android::base::LogMessage(__FILE__, __LINE__, SEVERITY_LAMBDA(severity), \
+ _LOG_TAG_INTERNAL, errno) \
+ .stream()
+
+// Marker that code is yet to be implemented.
+#define UNIMPLEMENTED(level) \
+ LOG(level) << __PRETTY_FUNCTION__ << " unimplemented "
+
+// Check whether condition x holds and LOG(FATAL) if not. The value of the
+// expression x is only evaluated once. Extra logging can be appended using <<
+// after. For example:
+//
+// CHECK(false == true) results in a log message of
+// "Check failed: false == true".
+#define CHECK(x) \
+ LIKELY((x)) || ABORT_AFTER_LOG_FATAL_EXPR(false) || \
+ ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::FATAL, _LOG_TAG_INTERNAL, \
+ -1) \
+ .stream() \
+ << "Check failed: " #x << " "
+
+// clang-format off
+// Helper for CHECK_xx(x,y) macros.
+#define CHECK_OP(LHS, RHS, OP) \
+ for (auto _values = ::android::base::MakeEagerEvaluator(LHS, RHS); \
+ UNLIKELY(!(_values.lhs.v OP _values.rhs.v)); \
+ /* empty */) \
+ ABORT_AFTER_LOG_FATAL \
+ ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::FATAL, _LOG_TAG_INTERNAL, -1) \
+ .stream() \
+ << "Check failed: " << #LHS << " " << #OP << " " << #RHS << " (" #LHS "=" \
+ << ::android::base::LogNullGuard::Guard(_values.lhs.v) \
+ << ", " #RHS "=" \
+ << ::android::base::LogNullGuard::Guard(_values.rhs.v) \
+ << ") "
+// clang-format on
+
+// Check whether a condition holds between x and y, LOG(FATAL) if not. The value
+// of the expressions x and y is evaluated once. Extra logging can be appended
+// using << after. For example:
+//
+// CHECK_NE(0 == 1, false) results in
+// "Check failed: false != false (0==1=false, false=false) ".
+#define CHECK_EQ(x, y) CHECK_OP(x, y, == )
+#define CHECK_NE(x, y) CHECK_OP(x, y, != )
+#define CHECK_LE(x, y) CHECK_OP(x, y, <= )
+#define CHECK_LT(x, y) CHECK_OP(x, y, < )
+#define CHECK_GE(x, y) CHECK_OP(x, y, >= )
+#define CHECK_GT(x, y) CHECK_OP(x, y, > )
+
+// clang-format off
+// Helper for CHECK_STRxx(s1,s2) macros.
+#define CHECK_STROP(s1, s2, sense) \
+ while (UNLIKELY((strcmp(s1, s2) == 0) != (sense))) \
+ ABORT_AFTER_LOG_FATAL \
+ ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::FATAL, \
+ _LOG_TAG_INTERNAL, -1) \
+ .stream() \
+ << "Check failed: " << "\"" << (s1) << "\"" \
+ << ((sense) ? " == " : " != ") << "\"" << (s2) << "\""
+// clang-format on
+
+// Check for string (const char*) equality between s1 and s2, LOG(FATAL) if not.
+#define CHECK_STREQ(s1, s2) CHECK_STROP(s1, s2, true)
+#define CHECK_STRNE(s1, s2) CHECK_STROP(s1, s2, false)
+
+// Perform the pthread function call(args), LOG(FATAL) on error.
+#define CHECK_PTHREAD_CALL(call, args, what) \
+ do { \
+ int rc = call args; \
+ if (rc != 0) { \
+ errno = rc; \
+ ABORT_AFTER_LOG_FATAL \
+ PLOG(FATAL) << #call << " failed for " << (what); \
+ } \
+ } while (false)
+
+// DCHECKs are debug variants of CHECKs only enabled in debug builds. Generally
+// CHECK should be used unless profiling identifies a CHECK as being in
+// performance critical code.
+#if defined(NDEBUG) && !defined(__clang_analyzer__)
+static constexpr bool kEnableDChecks = false;
+#else
+static constexpr bool kEnableDChecks = true;
+#endif
+
+#define DCHECK(x) \
+ if (::android::base::kEnableDChecks) CHECK(x)
+#define DCHECK_EQ(x, y) \
+ if (::android::base::kEnableDChecks) CHECK_EQ(x, y)
+#define DCHECK_NE(x, y) \
+ if (::android::base::kEnableDChecks) CHECK_NE(x, y)
+#define DCHECK_LE(x, y) \
+ if (::android::base::kEnableDChecks) CHECK_LE(x, y)
+#define DCHECK_LT(x, y) \
+ if (::android::base::kEnableDChecks) CHECK_LT(x, y)
+#define DCHECK_GE(x, y) \
+ if (::android::base::kEnableDChecks) CHECK_GE(x, y)
+#define DCHECK_GT(x, y) \
+ if (::android::base::kEnableDChecks) CHECK_GT(x, y)
+#define DCHECK_STREQ(s1, s2) \
+ if (::android::base::kEnableDChecks) CHECK_STREQ(s1, s2)
+#define DCHECK_STRNE(s1, s2) \
+ if (::android::base::kEnableDChecks) CHECK_STRNE(s1, s2)
+
+namespace log_detail {
+
+// Temporary storage for a single eagerly evaluated check expression operand.
+template struct Storage {
+ template explicit constexpr Storage(U&& u) : v(std::forward(u)) {}
+ explicit Storage(const Storage& t) = delete;
+ explicit Storage(Storage&& t) = delete;
+ T v;
+};
+
+// Partial specialization for smart pointers to avoid copying.
+template struct Storage> {
+ explicit constexpr Storage(const std::unique_ptr& ptr) : v(ptr.get()) {}
+ const T* v;
+};
+template struct Storage> {
+ explicit constexpr Storage(const std::shared_ptr& ptr) : v(ptr.get()) {}
+ const T* v;
+};
+
+// Type trait that checks if a type is a (potentially const) char pointer.
+template struct IsCharPointer {
+ using Pointee = std::remove_cv_t>;
+ static constexpr bool value = std::is_pointer_v &&
+ (std::is_same_v || std::is_same_v ||
+ std::is_same_v);
+};
+
+// Counterpart to Storage that depends on both operands. This is used to prevent
+// char pointers being treated as strings in the log output - they might point
+// to buffers of unprintable binary data.
+template struct StorageTypes {
+ static constexpr bool voidptr = IsCharPointer::value && IsCharPointer::value;
+ using LHSType = std::conditional_t;
+ using RHSType = std::conditional_t;
+};
+
+// Temporary class created to evaluate the LHS and RHS, used with
+// MakeEagerEvaluator to infer the types of LHS and RHS.
+template
+struct EagerEvaluator {
+ template constexpr EagerEvaluator(A&& l, B&& r)
+ : lhs(std::forward(l)), rhs(std::forward(r)) {}
+ const Storage::LHSType> lhs;
+ const Storage::RHSType> rhs;
+};
+
+} // namespace log_detail
+
+// Converts std::nullptr_t and null char pointers to the string "null"
+// when writing the failure message.
+template struct LogNullGuard {
+ static const T& Guard(const T& v) { return v; }
+};
+template <> struct LogNullGuard {
+ static const char* Guard(const std::nullptr_t&) { return "(null)"; }
+};
+template <> struct LogNullGuard {
+ static const char* Guard(const char* v) { return v ? v : "(null)"; }
+};
+template <> struct LogNullGuard {
+ static const char* Guard(const char* v) { return v ? v : "(null)"; }
+};
+
+// Helper function for CHECK_xx.
+template
+constexpr auto MakeEagerEvaluator(LHS&& lhs, RHS&& rhs) {
+ return log_detail::EagerEvaluator, std::decay_t>(
+ std::forward(lhs), std::forward(rhs));
+}
+
+// Data for the log message, not stored in LogMessage to avoid increasing the
+// stack size.
+class LogMessageData;
+
+// A LogMessage is a temporarily scoped object used by LOG and the unlikely part
+// of a CHECK. The destructor will abort if the severity is FATAL.
+class LogMessage {
+ public:
+ // LogId has been deprecated, but this constructor must exist for prebuilts.
+ LogMessage(const char* file, unsigned int line, LogId, LogSeverity severity, const char* tag,
+ int error);
+ LogMessage(const char* file, unsigned int line, LogSeverity severity, const char* tag, int error);
+
+ ~LogMessage();
+
+ // Returns the stream associated with the message, the LogMessage performs
+ // output when it goes out of scope.
+ std::ostream& stream();
+
+ // The routine that performs the actual logging.
+ static void LogLine(const char* file, unsigned int line, LogSeverity severity, const char* tag,
+ const char* msg);
+
+ private:
+ const std::unique_ptr data_;
+
+ DISALLOW_COPY_AND_ASSIGN(LogMessage);
+};
+
+// Get the minimum severity level for logging.
+LogSeverity GetMinimumLogSeverity();
+
+// Set the minimum severity level for logging, returning the old severity.
+LogSeverity SetMinimumLogSeverity(LogSeverity new_severity);
+
+// Return whether or not a log message with the associated tag should be logged.
+bool ShouldLog(LogSeverity severity, const char* tag);
+
+// Allows to temporarily change the minimum severity level for logging.
+class ScopedLogSeverity {
+ public:
+ explicit ScopedLogSeverity(LogSeverity level);
+ ~ScopedLogSeverity();
+
+ private:
+ LogSeverity old_;
+};
+
+} // namespace base
+} // namespace android
+
+namespace std { // NOLINT(cert-dcl58-cpp)
+
+// Emit a warning of ostream<< with std::string*. The intention was most likely to print *string.
+//
+// Note: for this to work, we need to have this in a namespace.
+// Note: using a pragma because "-Wgcc-compat" (included in "-Weverything") complains about
+// diagnose_if.
+// Note: to print the pointer, use "<< static_cast(string_pointer)" instead.
+// Note: a not-recommended alternative is to let Clang ignore the warning by adding
+// -Wno-user-defined-warnings to CPPFLAGS.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgcc-compat"
+#define OSTREAM_STRING_POINTER_USAGE_WARNING \
+ __attribute__((diagnose_if(true, "Unexpected logging of string pointer", "warning")))
+inline OSTREAM_STRING_POINTER_USAGE_WARNING
+std::ostream& operator<<(std::ostream& stream, const std::string* string_pointer) {
+ return stream << static_cast(string_pointer);
+}
+#pragma clang diagnostic pop
+
+} // namespace std
diff --git a/base/src/main/cpp/include/base/macros.h b/base/src/main/cpp/include/base/macros.h
new file mode 100644
index 000000000..b9ee402f2
--- /dev/null
+++ b/base/src/main/cpp/include/base/macros.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include // for size_t
+#include // for TEMP_FAILURE_RETRY
+
+#include
+
+// bionic and glibc both have TEMP_FAILURE_RETRY, but eg Mac OS' libc doesn't.
+#ifndef TEMP_FAILURE_RETRY
+#define TEMP_FAILURE_RETRY(exp) \
+ ({ \
+ decltype(exp) _rc; \
+ do { \
+ _rc = (exp); \
+ } while (_rc == -1 && errno == EINTR); \
+ _rc; \
+ })
+#endif
+
+// A macro to disallow the copy constructor and operator= functions
+// This must be placed in the private: declarations for a class.
+//
+// For disallowing only assign or copy, delete the relevant operator or
+// constructor, for example:
+// void operator=(const TypeName&) = delete;
+// Note, that most uses of DISALLOW_ASSIGN and DISALLOW_COPY are broken
+// semantically, one should either use disallow both or neither. Try to
+// avoid these in new code.
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName&) = delete; \
+ void operator=(const TypeName&) = delete
+
+// A macro to disallow all the implicit constructors, namely the
+// default constructor, copy constructor and operator= functions.
+//
+// This should be used in the private: declarations for a class
+// that wants to prevent anyone from instantiating it. This is
+// especially useful for classes containing only static methods.
+#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
+ TypeName() = delete; \
+ DISALLOW_COPY_AND_ASSIGN(TypeName)
+
+// The arraysize(arr) macro returns the # of elements in an array arr.
+// The expression is a compile-time constant, and therefore can be
+// used in defining new arrays, for example. If you use arraysize on
+// a pointer by mistake, you will get a compile-time error.
+//
+// One caveat is that arraysize() doesn't accept any array of an
+// anonymous type or a type defined inside a function. In these rare
+// cases, you have to use the unsafe ARRAYSIZE_UNSAFE() macro below. This is
+// due to a limitation in C++'s template system. The limitation might
+// eventually be removed, but it hasn't happened yet.
+
+// This template function declaration is used in defining arraysize.
+// Note that the function doesn't need an implementation, as we only
+// use its type.
+template
+char(&ArraySizeHelper(T(&array)[N]))[N]; // NOLINT(readability/casting)
+
+#define arraysize(array) (sizeof(ArraySizeHelper(array)))
+
+#define SIZEOF_MEMBER(t, f) sizeof(std::declval().f)
+
+// Changing this definition will cause you a lot of pain. A majority of
+// vendor code defines LIKELY and UNLIKELY this way, and includes
+// this header through an indirect path.
+#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true ))
+#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false ))
+
+#define WARN_UNUSED __attribute__((warn_unused_result))
+
+// A deprecated function to call to create a false use of the parameter, for
+// example:
+// int foo(int x) { UNUSED(x); return 10; }
+// to avoid compiler warnings. Going forward we prefer ATTRIBUTE_UNUSED.
+template
+void UNUSED(const T&...) {
+}
+
+// An attribute to place on a parameter to a function, for example:
+// int foo(int x ATTRIBUTE_UNUSED) { return 10; }
+// to avoid compiler warnings.
+#define ATTRIBUTE_UNUSED __attribute__((__unused__))
+
+// The FALLTHROUGH_INTENDED macro can be used to annotate implicit fall-through
+// between switch labels:
+// switch (x) {
+// case 40:
+// case 41:
+// if (truth_is_out_there) {
+// ++x;
+// FALLTHROUGH_INTENDED; // Use instead of/along with annotations in
+// // comments.
+// } else {
+// return x;
+// }
+// case 42:
+// ...
+//
+// As shown in the example above, the FALLTHROUGH_INTENDED macro should be
+// followed by a semicolon. It is designed to mimic control-flow statements
+// like 'break;', so it can be placed in most places where 'break;' can, but
+// only if there are no statements on the execution path between it and the
+// next switch label.
+//
+// When compiled with clang, the FALLTHROUGH_INTENDED macro is expanded to
+// [[clang::fallthrough]] attribute, which is analysed when performing switch
+// labels fall-through diagnostic ('-Wimplicit-fallthrough'). See clang
+// documentation on language extensions for details:
+// http://clang.llvm.org/docs/LanguageExtensions.html#clang__fallthrough
+//
+// When used with unsupported compilers, the FALLTHROUGH_INTENDED macro has no
+// effect on diagnostics.
+//
+// In either case this macro has no effect on runtime behavior and performance
+// of code.
+#ifndef FALLTHROUGH_INTENDED
+#define FALLTHROUGH_INTENDED [[fallthrough]] // NOLINT
+#endif
+
+// Current ABI string
+#if defined(__arm__)
+#define ABI_STRING "arm"
+#elif defined(__aarch64__)
+#define ABI_STRING "arm64"
+#elif defined(__i386__)
+#define ABI_STRING "x86"
+#elif defined(__riscv)
+#define ABI_STRING "riscv64"
+#elif defined(__x86_64__)
+#define ABI_STRING "x86_64"
+#endif
diff --git a/base/src/main/cpp/logging.cpp b/base/src/main/cpp/logging.cpp
new file mode 100644
index 000000000..b930dece0
--- /dev/null
+++ b/base/src/main/cpp/logging.cpp
@@ -0,0 +1,490 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if defined(_WIN32)
+#include
+#endif
+
+#include "base/logging.h"
+
+#include
+#include
+#include
+#include
+
+// For getprogname(3) or program_invocation_short_name.
+#if defined(__ANDROID__) || defined(__APPLE__)
+#include
+#elif defined(__GLIBC__)
+#include
+#endif
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#ifdef __ANDROID__
+#include
+#else
+#include
+#include
+#endif
+
+#include "strings.h"
+#include "logging_splitters.h"
+
+namespace android {
+namespace base {
+
+// BSD-based systems like Android/macOS have getprogname(). Others need us to provide one.
+#if !defined(__APPLE__) && !defined(__BIONIC__)
+static const char* getprogname() {
+#ifdef _WIN32
+ static bool first = true;
+ static char progname[MAX_PATH] = {};
+
+ if (first) {
+ snprintf(progname, sizeof(progname), "%s",
+ android::base::Basename(android::base::GetExecutablePath()).c_str());
+ first = false;
+ }
+
+ return progname;
+#else
+ return program_invocation_short_name;
+#endif
+}
+#endif
+
+static const char* GetFileBasename(const char* file) {
+ // We can't use basename(3) even on Unix because the Mac doesn't
+ // have a non-modifying basename.
+ const char* last_slash = strrchr(file, '/');
+ if (last_slash != nullptr) {
+ return last_slash + 1;
+ }
+#if defined(_WIN32)
+ const char* last_backslash = strrchr(file, '\\');
+ if (last_backslash != nullptr) {
+ return last_backslash + 1;
+ }
+#endif
+ return file;
+}
+
+static LogId log_id_tToLogId(int32_t 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 int32_t 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 int32_t 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 LogFunction& Logger() {
+#ifdef __ANDROID__
+ static auto& logger = *new LogFunction(LogdLogger());
+#else
+ static auto& logger = *new LogFunction(StderrLogger);
+#endif
+ return logger;
+}
+
+static AbortFunction& Aborter() {
+ static auto& aborter = *new AbortFunction(DefaultAborter);
+ return aborter;
+}
+
+// Only used for Q fallback.
+static std::recursive_mutex& TagLock() {
+ static auto& tag_lock = *new std::recursive_mutex();
+ return tag_lock;
+}
+// Only used for Q fallback.
+static std::string* gDefaultTag;
+
+void SetDefaultTag(const std::string& tag) {
+ if (__builtin_available(android 30, *)) {
+ __android_log_set_default_tag(tag.c_str());
+ } else {
+ std::lock_guard 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;
+
+LogFunction TeeLogger(LogFunction&& l1, LogFunction&& l2) {
+ return [l1 = std::move(l1), l2 = std::move(l2)](LogId id, LogSeverity severity, const char* tag,
+ const char* file, unsigned int line,
+ const char* message) {
+ l1(id, severity, tag, file, line, message);
+ l2(id, severity, tag, file, line, message);
+ };
+}
+
+void DefaultAborter(const char* abort_message) {
+#ifdef __ANDROID__
+ android_set_abort_message(abort_message);
+#else
+ UNUSED(abort_message);
+#endif
+ abort();
+}
+
+static void LogdLogChunk(LogId id, LogSeverity severity, const char* tag, const char* message) {
+ int32_t lg_id = LogIdTolog_id_t(id);
+ int32_t priority = LogSeverityToPriority(severity);
+
+ if (__builtin_available(android 30, *)) {
+ __android_log_message log_message = {sizeof(__android_log_message), lg_id, priority, tag,
+ static_cast(nullptr), 0, message};
+ __android_log_logd_logger(&log_message);
+ } else {
+ __android_log_buf_print(lg_id, priority, tag, "%s", message);
+ }
+}
+
+LogdLogger::LogdLogger(LogId default_log_id) : default_log_id_(default_log_id) {}
+
+void LogdLogger::operator()(LogId id, LogSeverity severity, const char* tag, const char* file,
+ unsigned int line, const char* message) {
+ if (id == DEFAULT) {
+ id = default_log_id_;
+ }
+
+ SplitByLogdChunks(id, severity, tag, file, line, message, LogdLogChunk);
+}
+
+void InitLogging(char* argv[], LogFunction&& logger, AbortFunction&& aborter) {
+ SetLogger(std::forward(logger));
+ SetAborter(std::forward(aborter));
+
+ if (gInitialized) {
+ return;
+ }
+
+ gInitialized = true;
+
+ // Stash the command line for later use. We can use /proc/self/cmdline on
+ // Linux to recover this, but we don't have that luxury on the Mac/Windows,
+ // and there are a couple of argv[0] variants that are commonly used.
+ if (argv != nullptr) {
+ SetDefaultTag(basename(argv[0]));
+ }
+
+ const char* tags = getenv("ANDROID_LOG_TAGS");
+ if (tags == nullptr) {
+ return;
+ }
+
+ std::vector specs = Split(tags, " ");
+ for (size_t i = 0; i < specs.size(); ++i) {
+ // "tag-pattern:[vdiwefs]"
+ std::string spec(specs[i]);
+ if (spec.size() == 3 && StartsWith(spec, "*:")) {
+ switch (spec[2]) {
+ case 'v':
+ SetMinimumLogSeverity(VERBOSE);
+ continue;
+ case 'd':
+ SetMinimumLogSeverity(DEBUG);
+ continue;
+ case 'i':
+ SetMinimumLogSeverity(INFO);
+ continue;
+ case 'w':
+ SetMinimumLogSeverity(WARNING);
+ continue;
+ case 'e':
+ SetMinimumLogSeverity(ERROR);
+ continue;
+ case 'f':
+ SetMinimumLogSeverity(FATAL_WITHOUT_ABORT);
+ continue;
+ // liblog will even suppress FATAL if you say 's' for silent, but fatal should
+ // never be suppressed.
+ case 's':
+ SetMinimumLogSeverity(FATAL_WITHOUT_ABORT);
+ continue;
+ }
+ }
+ LOG(FATAL) << "unsupported '" << spec << "' in ANDROID_LOG_TAGS (" << tags
+ << ")";
+ }
+}
+
+LogFunction SetLogger(LogFunction&& logger) {
+ LogFunction old_logger = std::move(Logger());
+ Logger() = std::move(logger);
+
+ if (__builtin_available(android 30, *)) {
+ __android_log_set_logger([](const struct __android_log_message* log_message) {
+ auto log_id = log_id_tToLogId(log_message->buffer_id);
+ auto severity = PriorityToLogSeverity(log_message->priority);
+
+ Logger()(log_id, severity, log_message->tag, log_message->file, log_message->line,
+ log_message->message);
+ });
+ }
+ return old_logger;
+}
+
+AbortFunction SetAborter(AbortFunction&& aborter) {
+ AbortFunction old_aborter = std::move(Aborter());
+ Aborter() = std::move(aborter);
+
+ if (__builtin_available(android 30, *)) {
+ __android_log_set_aborter([](const char* abort_message) { Aborter()(abort_message); });
+ }
+ return old_aborter;
+}
+
+// This indirection greatly reduces the stack impact of having lots of
+// checks/logging in a function.
+class LogMessageData {
+ public:
+ LogMessageData(const char* file, unsigned int line, LogSeverity severity, const char* tag,
+ int error)
+ : file_(GetFileBasename(file)),
+ line_number_(line),
+ severity_(severity),
+ tag_(tag),
+ error_(error) {}
+
+ const char* GetFile() const {
+ return file_;
+ }
+
+ unsigned int GetLineNumber() const {
+ return line_number_;
+ }
+
+ LogSeverity GetSeverity() const {
+ return severity_;
+ }
+
+ const char* GetTag() const { return tag_; }
+
+ int GetError() const {
+ return error_;
+ }
+
+ std::ostream& GetBuffer() {
+ return buffer_;
+ }
+
+ std::string ToString() const {
+ return buffer_.str();
+ }
+
+ private:
+ std::ostringstream buffer_;
+ const char* const file_;
+ const unsigned int line_number_;
+ const LogSeverity severity_;
+ const char* const tag_;
+ const int error_;
+
+ DISALLOW_COPY_AND_ASSIGN(LogMessageData);
+};
+
+LogMessage::LogMessage(const char* file, unsigned int line, LogId, LogSeverity severity,
+ const char* tag, int error)
+ : LogMessage(file, line, severity, tag, error) {}
+
+LogMessage::LogMessage(const char* file, unsigned int line, LogSeverity severity, const char* tag,
+ int error)
+ : data_(new LogMessageData(file, line, severity, tag, error)) {}
+
+LogMessage::~LogMessage() {
+ // Check severity again. This is duplicate work wrt/ LOG macros, but not LOG_STREAM.
+ if (!WOULD_LOG(data_->GetSeverity())) {
+ return;
+ }
+
+ // Finish constructing the message.
+ if (data_->GetError() != -1) {
+ data_->GetBuffer() << ": " << strerror(data_->GetError());
+ }
+ std::string msg(data_->ToString());
+
+ if (data_->GetSeverity() == FATAL) {
+#ifdef __ANDROID__
+ // Set the bionic abort message early to avoid liblog doing it
+ // with the individual lines, so that we get the whole message.
+ android_set_abort_message(msg.c_str());
+#endif
+ }
+
+ LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), data_->GetTag(),
+ msg.c_str());
+
+ // Abort if necessary.
+ if (data_->GetSeverity() == FATAL) {
+ if (__builtin_available(android 30, *)) {
+ __android_log_call_aborter(msg.c_str());
+ } else {
+ Aborter()(msg.c_str());
+ }
+ }
+}
+
+std::ostream& LogMessage::stream() {
+ return data_->GetBuffer();
+}
+
+void LogMessage::LogLine(const char* file, unsigned int line, LogSeverity severity, const char* tag,
+ const char* message) {
+ int32_t priority = LogSeverityToPriority(severity);
+ if (__builtin_available(android 30, *)) {
+ __android_log_message log_message = {
+ sizeof(__android_log_message), LOG_ID_DEFAULT, priority, tag, file, line, message};
+ __android_log_write_log_message(&log_message);
+ } else {
+ if (tag == nullptr) {
+ std::lock_guard 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() {
+ if (__builtin_available(android 30, *)) {
+ return PriorityToLogSeverity(__android_log_get_minimum_priority());
+ } else {
+ return gMinimumLogSeverity;
+ }
+}
+
+bool ShouldLog(LogSeverity severity, const char* tag) {
+ // 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 (__builtin_available(android 30, *)) {
+ int32_t priority = LogSeverityToPriority(severity);
+ return __android_log_is_loggable(priority, tag, ANDROID_LOG_INFO);
+ } else {
+ return severity >= gMinimumLogSeverity;
+ }
+}
+
+LogSeverity SetMinimumLogSeverity(LogSeverity new_severity) {
+ if (__builtin_available(android 30, *)) {
+ int32_t priority = LogSeverityToPriority(new_severity);
+ return PriorityToLogSeverity(__android_log_set_minimum_priority(priority));
+ } else {
+ LogSeverity old_severity = gMinimumLogSeverity;
+ gMinimumLogSeverity = new_severity;
+ return old_severity;
+ }
+}
+
+ScopedLogSeverity::ScopedLogSeverity(LogSeverity new_severity) {
+ old_ = SetMinimumLogSeverity(new_severity);
+}
+
+ScopedLogSeverity::~ScopedLogSeverity() {
+ SetMinimumLogSeverity(old_);
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/src/main/cpp/logging_splitters.h b/base/src/main/cpp/logging_splitters.h
new file mode 100644
index 000000000..01bbd8c9b
--- /dev/null
+++ b/base/src/main/cpp/logging_splitters.h
@@ -0,0 +1,135 @@
+/*
+ * 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
+#include
+#include
+
+#include "stringprintf.h"
+
+#define LOGGER_ENTRY_MAX_PAYLOAD 4068 // This constant is not in the NDK.
+
+namespace android {
+namespace base {
+
+// This splits the message up line by line, by calling log_function with a pointer to the start of
+// each line and the size up to the newline character. It sends size = -1 for the final line.
+template
+static void SplitByLines(const char* msg, const F& log_function, Args&&... args) {
+ const char* newline = strchr(msg, '\n');
+ while (newline != nullptr) {
+ log_function(msg, newline - msg, args...);
+ msg = newline + 1;
+ newline = strchr(msg, '\n');
+ }
+
+ log_function(msg, -1, args...);
+}
+
+// This splits the message up into chunks that logs can process delimited by new lines. It calls
+// log_function with the exact null terminated message that should be sent to logd.
+// Note, despite the loops and snprintf's, if severity is not fatal and there are no new lines,
+// this function simply calls log_function with msg without any extra overhead.
+template
+static void SplitByLogdChunks(LogId log_id, LogSeverity severity, const char* tag, const char* file,
+ unsigned int line, const char* msg, const F& log_function) {
+ // The maximum size of a payload, after the log header that logd will accept is
+ // LOGGER_ENTRY_MAX_PAYLOAD, so subtract the other elements in the payload to find the size of
+ // the string that we can log in each pass.
+ // The protocol is documented in liblog/README.protocol.md.
+ // Specifically we subtract a byte for the priority, the length of the tag + its null terminator,
+ // and an additional byte for the null terminator on the payload. We subtract an additional 32
+ // bytes for slack, similar to java/android/util/Log.java.
+ ptrdiff_t max_size = LOGGER_ENTRY_MAX_PAYLOAD - strlen(tag) - 35;
+ if (max_size <= 0) {
+ abort();
+ }
+ // If we're logging a fatal message, we'll append the file and line numbers.
+ bool add_file = file != nullptr && (severity == FATAL || severity == FATAL_WITHOUT_ABORT);
+
+ std::string file_header;
+ if (add_file) {
+ file_header = StringPrintf("%s:%u] ", file, line);
+ }
+ int file_header_size = file_header.size();
+
+ __attribute__((uninitialized)) char logd_chunk[max_size + 1];
+ ptrdiff_t chunk_position = 0;
+
+ auto call_log_function = [&]() {
+ log_function(log_id, severity, tag, logd_chunk);
+ chunk_position = 0;
+ };
+
+ auto write_to_logd_chunk = [&](const char* message, int length) {
+ int size_written = 0;
+ const char* new_line = chunk_position > 0 ? "\n" : "";
+ if (add_file) {
+ size_written = snprintf(logd_chunk + chunk_position, sizeof(logd_chunk) - chunk_position,
+ "%s%s%.*s", new_line, file_header.c_str(), length, message);
+ } else {
+ size_written = snprintf(logd_chunk + chunk_position, sizeof(logd_chunk) - chunk_position,
+ "%s%.*s", new_line, length, message);
+ }
+
+ // This should never fail, if it does and we set size_written to 0, which will skip this line
+ // and move to the next one.
+ if (size_written < 0) {
+ size_written = 0;
+ }
+ chunk_position += size_written;
+ };
+
+ const char* newline = strchr(msg, '\n');
+ while (newline != nullptr) {
+ // If we have data in the buffer and this next line doesn't fit, write the buffer.
+ if (chunk_position != 0 && chunk_position + (newline - msg) + 1 + file_header_size > max_size) {
+ call_log_function();
+ }
+
+ // Otherwise, either the next line fits or we have any empty buffer and too large of a line to
+ // ever fit, in both cases, we add it to the buffer and continue.
+ write_to_logd_chunk(msg, newline - msg);
+
+ msg = newline + 1;
+ newline = strchr(msg, '\n');
+ }
+
+ // If we have left over data in the buffer and we can fit the rest of msg, add it to the buffer
+ // then write the buffer.
+ if (chunk_position != 0 &&
+ chunk_position + static_cast(strlen(msg)) + 1 + file_header_size <= max_size) {
+ write_to_logd_chunk(msg, -1);
+ call_log_function();
+ } else {
+ // If the buffer is not empty and we can't fit the rest of msg into it, write its contents.
+ if (chunk_position != 0) {
+ call_log_function();
+ }
+ // Then write the rest of the msg.
+ if (add_file) {
+ snprintf(logd_chunk, sizeof(logd_chunk), "%s%s", file_header.c_str(), msg);
+ log_function(log_id, severity, tag, logd_chunk);
+ } else {
+ log_function(log_id, severity, tag, msg);
+ }
+ }
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/src/main/cpp/stringprintf.cpp b/base/src/main/cpp/stringprintf.cpp
new file mode 100644
index 000000000..84b7b1372
--- /dev/null
+++ b/base/src/main/cpp/stringprintf.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "stringprintf.h"
+
+#include
+
+#include
+
+namespace android {
+namespace base {
+
+void StringAppendV(std::string* dst, const char* format, va_list ap) {
+ // First try with a small fixed size buffer
+ char space[1024] __attribute__((__uninitialized__));
+
+ // It's possible for methods that use a va_list to invalidate
+ // the data in it upon use. The fix is to make a copy
+ // of the structure before using it and use that copy instead.
+ va_list backup_ap;
+ va_copy(backup_ap, ap);
+ int result = vsnprintf(space, sizeof(space), format, backup_ap);
+ va_end(backup_ap);
+
+ if (result < static_cast(sizeof(space))) {
+ if (result >= 0) {
+ // Normal case -- everything fit.
+ dst->append(space, result);
+ return;
+ }
+
+ if (result < 0) {
+ // Just an error.
+ return;
+ }
+ }
+
+ // Increase the buffer size to the size requested by vsnprintf,
+ // plus one for the closing \0.
+ int length = result + 1;
+ char* buf = new char[length];
+
+ // Restore the va_list before we use it again
+ va_copy(backup_ap, ap);
+ result = vsnprintf(buf, length, format, backup_ap);
+ va_end(backup_ap);
+
+ if (result >= 0 && result < length) {
+ // It fit
+ dst->append(buf, result);
+ }
+ delete[] buf;
+}
+
+std::string StringPrintf(const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ std::string result;
+ StringAppendV(&result, fmt, ap);
+ va_end(ap);
+ return result;
+}
+
+void StringAppendF(std::string* dst, const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ StringAppendV(dst, format, ap);
+ va_end(ap);
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/src/main/cpp/stringprintf.h b/base/src/main/cpp/stringprintf.h
new file mode 100644
index 000000000..93c56afd7
--- /dev/null
+++ b/base/src/main/cpp/stringprintf.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include
+#include
+
+namespace android {
+namespace base {
+
+// These printf-like functions are implemented in terms of vsnprintf, so they
+// use the same attribute for compile-time format string checking.
+
+// Returns a string corresponding to printf-like formatting of the arguments.
+std::string StringPrintf(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)));
+
+// Appends a printf-like formatting of the arguments to 'dst'.
+void StringAppendF(std::string* dst, const char* fmt, ...)
+ __attribute__((__format__(__printf__, 2, 3)));
+
+// Appends a printf-like formatting of the arguments to 'dst'.
+void StringAppendV(std::string* dst, const char* format, va_list ap)
+ __attribute__((__format__(__printf__, 2, 0)));
+
+} // namespace base
+} // namespace android
diff --git a/base/src/main/cpp/strings.cpp b/base/src/main/cpp/strings.cpp
new file mode 100644
index 000000000..80bac65b4
--- /dev/null
+++ b/base/src/main/cpp/strings.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "strings.h"
+
+#include
+#include
+
+#include
+#include
+
+#include "stringprintf.h"
+
+namespace android {
+namespace base {
+
+#define CHECK_NE(a, b) \
+ if ((a) == (b)) abort();
+
+std::vector Split(const std::string& s,
+ const std::string& delimiters) {
+ CHECK_NE(delimiters.size(), 0U);
+
+ std::vector result;
+
+ size_t base = 0;
+ size_t found;
+ while (true) {
+ found = s.find_first_of(delimiters, base);
+ result.push_back(s.substr(base, found - base));
+ if (found == s.npos) break;
+ base = found + 1;
+ }
+
+ return result;
+}
+
+std::vector Tokenize(const std::string& s, const std::string& delimiters) {
+ CHECK_NE(delimiters.size(), 0U);
+
+ std::vector result;
+ size_t end = 0;
+
+ while (true) {
+ size_t base = s.find_first_not_of(delimiters, end);
+ if (base == s.npos) {
+ break;
+ }
+ end = s.find_first_of(delimiters, base);
+ result.push_back(s.substr(base, end - base));
+ }
+ return result;
+}
+
+[[deprecated("Retained only for binary compatibility (symbol name)")]]
+std::string Trim(const std::string& s) {
+ return Trim(std::string_view(s));
+}
+
+template std::string Trim(const char*&);
+template std::string Trim(const char*&&);
+template std::string Trim(const std::string&);
+template std::string Trim(const std::string&&);
+template std::string Trim(std::string_view&);
+template std::string Trim(std::string_view&&);
+
+// These cases are probably the norm, so we mark them extern in the header to
+// aid compile time and binary size.
+template std::string Join(const std::vector&, char);
+template std::string Join(const std::vector&, char);
+template std::string Join(const std::vector&, const std::string&);
+template std::string Join(const std::vector&, const std::string&);
+
+bool StartsWith(std::string_view s, std::string_view prefix) {
+ return s.substr(0, prefix.size()) == prefix;
+}
+
+bool StartsWith(std::string_view s, char prefix) {
+ return !s.empty() && s.front() == prefix;
+}
+
+bool StartsWithIgnoreCase(std::string_view s, std::string_view prefix) {
+ return s.size() >= prefix.size() && strncasecmp(s.data(), prefix.data(), prefix.size()) == 0;
+}
+
+bool EndsWith(std::string_view s, std::string_view suffix) {
+ return s.size() >= suffix.size() && s.substr(s.size() - suffix.size(), suffix.size()) == suffix;
+}
+
+bool EndsWith(std::string_view s, char suffix) {
+ return !s.empty() && s.back() == suffix;
+}
+
+bool EndsWithIgnoreCase(std::string_view s, std::string_view suffix) {
+ return s.size() >= suffix.size() &&
+ strncasecmp(s.data() + (s.size() - suffix.size()), suffix.data(), suffix.size()) == 0;
+}
+
+bool EqualsIgnoreCase(std::string_view lhs, std::string_view rhs) {
+ return lhs.size() == rhs.size() && strncasecmp(lhs.data(), rhs.data(), lhs.size()) == 0;
+}
+
+std::string StringReplace(std::string_view s, std::string_view from, std::string_view to,
+ bool all) {
+ if (from.empty()) return std::string(s);
+
+ std::string result;
+ std::string_view::size_type start_pos = 0;
+ do {
+ std::string_view::size_type pos = s.find(from, start_pos);
+ if (pos == std::string_view::npos) break;
+
+ result.append(s.data() + start_pos, pos - start_pos);
+ result.append(to.data(), to.size());
+
+ start_pos = pos + from.size();
+ } while (all);
+ result.append(s.data() + start_pos, s.size() - start_pos);
+ return result;
+}
+
+std::string ErrnoNumberAsString(int errnum) {
+ char buf[100];
+ buf[0] = '\0';
+ int strerror_err = strerror_r(errnum, buf, sizeof(buf));
+ if (strerror_err < 0) {
+ return StringPrintf("Failed to convert errno %d to string: %d", errnum, strerror_err);
+ }
+ return buf;
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/src/main/cpp/strings.h b/base/src/main/cpp/strings.h
new file mode 100644
index 000000000..9557fad61
--- /dev/null
+++ b/base/src/main/cpp/strings.h
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace android {
+namespace base {
+
+// Splits a string into a vector of strings.
+//
+// The string is split at each occurrence of a character in delimiters.
+//
+// The empty string is not a valid delimiter list.
+std::vector Split(const std::string& s,
+ const std::string& delimiters);
+
+// Splits a string into a vector of string tokens.
+//
+// The string is split at each occurrence of a character in delimiters.
+// Coalesce runs of delimiter bytes and ignore delimiter bytes at the start or
+// end of string. In other words, return only nonempty string tokens.
+// Use when you don't care about recovering the original string with Join().
+//
+// Example:
+// Tokenize(" foo bar ", " ") => {"foo", "bar"}
+// Join(Tokenize(" foo bar", " "), " ") => "foo bar"
+//
+// The empty string is not a valid delimiter list.
+std::vector Tokenize(const std::string& s, const std::string& delimiters);
+
+namespace internal {
+template
+constexpr bool always_false_v = false;
+}
+
+template
+std::string Trim(T&& t) {
+ std::string_view sv;
+ std::string s;
+ if constexpr (std::is_convertible_v) {
+ sv = std::forward(t);
+ } else if constexpr (std::is_convertible_v) {
+ // The previous version of this function allowed for types which are implicitly convertible
+ // to std::string but not to std::string_view. For these types we go through std::string first
+ // here in order to retain source compatibility.
+ s = t;
+ sv = s;
+ } else {
+ static_assert(internal::always_false_v,
+ "Implicit conversion to std::string or std::string_view not possible");
+ }
+
+ // Skip initial whitespace.
+ while (!sv.empty() && isspace(sv.front())) {
+ sv.remove_prefix(1);
+ }
+
+ // Skip terminating whitespace.
+ while (!sv.empty() && isspace(sv.back())) {
+ sv.remove_suffix(1);
+ }
+
+ return std::string(sv);
+}
+
+// We instantiate the common cases in strings.cpp.
+extern template std::string Trim(const char*&);
+extern template std::string Trim(const char*&&);
+extern template std::string Trim(const std::string&);
+extern template std::string Trim(const std::string&&);
+extern template std::string Trim(std::string_view&);
+extern template std::string Trim(std::string_view&&);
+
+// Joins a container of things into a single string, using the given separator.
+template
+std::string Join(const ContainerT& things, SeparatorT separator) {
+ if (things.empty()) {
+ return "";
+ }
+
+ std::ostringstream result;
+ result << *things.begin();
+ for (auto it = std::next(things.begin()); it != things.end(); ++it) {
+ result << separator << *it;
+ }
+ return result.str();
+}
+
+// We instantiate the common cases in strings.cpp.
+extern template std::string Join(const std::vector&, char);
+extern template std::string Join(const std::vector&, char);
+extern template std::string Join(const std::vector&, const std::string&);
+extern template std::string Join(const std::vector&, const std::string&);
+
+// Tests whether 's' starts with 'prefix'.
+bool StartsWith(std::string_view s, std::string_view prefix);
+bool StartsWith(std::string_view s, char prefix);
+bool StartsWithIgnoreCase(std::string_view s, std::string_view prefix);
+
+// Tests whether 's' ends with 'suffix'.
+bool EndsWith(std::string_view s, std::string_view suffix);
+bool EndsWith(std::string_view s, char suffix);
+bool EndsWithIgnoreCase(std::string_view s, std::string_view suffix);
+
+// Tests whether 'lhs' equals 'rhs', ignoring case.
+bool EqualsIgnoreCase(std::string_view lhs, std::string_view rhs);
+
+// Removes `prefix` from the start of the given string and returns true (if
+// it was present), false otherwise.
+inline bool ConsumePrefix(std::string_view* s, std::string_view prefix) {
+ if (!StartsWith(*s, prefix)) return false;
+ s->remove_prefix(prefix.size());
+ return true;
+}
+
+// Removes `suffix` from the end of the given string and returns true (if
+// it was present), false otherwise.
+inline bool ConsumeSuffix(std::string_view* s, std::string_view suffix) {
+ if (!EndsWith(*s, suffix)) return false;
+ s->remove_suffix(suffix.size());
+ return true;
+}
+
+// Replaces `from` with `to` in `s`, once if `all == false`, or as many times as
+// there are matches if `all == true`.
+[[nodiscard]] std::string StringReplace(std::string_view s, std::string_view from,
+ std::string_view to, bool all);
+
+// Converts an errno number to its error message string.
+std::string ErrnoNumberAsString(int errnum);
+
+} // namespace base
+} // namespace android
diff --git a/hello-gl2/app/build.gradle b/hello-gl2/app/build.gradle
index a6a6f7a8b..456f045a2 100644
--- a/hello-gl2/app/build.gradle
+++ b/hello-gl2/app/build.gradle
@@ -20,4 +20,12 @@ android {
path 'src/main/cpp/CMakeLists.txt'
}
}
+
+ buildFeatures {
+ prefab true
+ }
+}
+
+dependencies {
+ implementation(project(":base"))
}
diff --git a/hello-gl2/app/src/main/cpp/CMakeLists.txt b/hello-gl2/app/src/main/cpp/CMakeLists.txt
index c023ce61f..ce4885a30 100644
--- a/hello-gl2/app/src/main/cpp/CMakeLists.txt
+++ b/hello-gl2/app/src/main/cpp/CMakeLists.txt
@@ -1,5 +1,7 @@
cmake_minimum_required(VERSION 3.22.1)
+find_package(base REQUIRED CONFIG)
+
# now build app's shared lib
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
@@ -11,5 +13,7 @@ target_link_libraries(gl2jni
android
log
EGL
- GLESv2)
+ GLESv2
+ base::base
+)
diff --git a/hello-gl2/app/src/main/cpp/gl_code.cpp b/hello-gl2/app/src/main/cpp/gl_code.cpp
index 28a929d10..ad55bfdbb 100644
--- a/hello-gl2/app/src/main/cpp/gl_code.cpp
+++ b/hello-gl2/app/src/main/cpp/gl_code.cpp
@@ -24,13 +24,15 @@
#include
#include
+#include "base/logging.h"
+
#define LOG_TAG "libgl2jni"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
static void printGLString(const char* name, GLenum s) {
const char* v = (const char*)glGetString(s);
- LOGI("GL %s = %s\n", name, v);
+ LOG(INFO) << "GL " << name << " = " << v;
}
static void checkGlError(const char* op) {
diff --git a/settings.gradle b/settings.gradle
index 1bac1cdee..f0d4b009a 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -22,6 +22,7 @@ dependencyResolutionManagement {
rootProject.name = "NDK Samples"
include(":audio-echo:app")
+include(":base")
include(":bitmap-plasma:app")
include(":camera:basic")
include(":camera:texture-view")