[core] Refactor resource hook & remove useless files (#281)
This commit is contained in:
parent
e72414b08c
commit
f3680cdd07
|
|
@ -1,7 +1,6 @@
|
||||||
cmake_minimum_required(VERSION 3.4.1)
|
cmake_minimum_required(VERSION 3.4.1)
|
||||||
|
|
||||||
add_subdirectory(yahfa)
|
add_subdirectory(yahfa)
|
||||||
add_subdirectory(android)
|
|
||||||
|
|
||||||
macro(SET_OPTION option value)
|
macro(SET_OPTION option value)
|
||||||
set(${option} ${value} CACHE INTERNAL "" FORCE)
|
set(${option} ${value} CACHE INTERNAL "" FORCE)
|
||||||
|
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
cmake_minimum_required(VERSION 3.4.1)
|
|
||||||
|
|
||||||
set(SRC_LIST
|
|
||||||
android-base/logging.cpp
|
|
||||||
android-base/file.cpp
|
|
||||||
android-base/threads.cpp
|
|
||||||
android-base/strings.cpp
|
|
||||||
)
|
|
||||||
add_library(android STATIC ${SRC_LIST})
|
|
||||||
|
|
||||||
target_include_directories(android PUBLIC .)
|
|
||||||
|
|
@ -1,427 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2015 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
#include "android-base/file.h"
|
|
||||||
#include <errno.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <ftw.h>
|
|
||||||
#include <libgen.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <memory>
|
|
||||||
#include <mutex>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#if defined(__APPLE__)
|
|
||||||
#include <mach-o/dyld.h>
|
|
||||||
#endif
|
|
||||||
#if defined(_WIN32)
|
|
||||||
#include <direct.h>
|
|
||||||
#include <windows.h>
|
|
||||||
#define O_NOFOLLOW 0
|
|
||||||
#define OS_PATH_SEPARATOR '\\'
|
|
||||||
#else
|
|
||||||
#define OS_PATH_SEPARATOR '/'
|
|
||||||
#endif
|
|
||||||
#include "android-base/logging.h" // and must be after windows.h for ERROR
|
|
||||||
#include "android-base/macros.h" // For TEMP_FAILURE_RETRY on Darwin.
|
|
||||||
#include "android-base/unique_fd.h"
|
|
||||||
#include "android-base/utf8.h"
|
|
||||||
#ifdef _WIN32
|
|
||||||
int mkstemp(char* template_name) {
|
|
||||||
if (_mktemp(template_name) == nullptr) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
// Use open() to match the close() that TemporaryFile's destructor does.
|
|
||||||
// Use O_BINARY to match base file APIs.
|
|
||||||
return open(template_name, O_CREAT | O_EXCL | O_RDWR | O_BINARY, S_IRUSR | S_IWUSR);
|
|
||||||
}
|
|
||||||
char* mkdtemp(char* template_name) {
|
|
||||||
if (_mktemp(template_name) == nullptr) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
if (_mkdir(template_name) == -1) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return template_name;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
namespace {
|
|
||||||
std::string GetSystemTempDir() {
|
|
||||||
#if defined(__ANDROID__)
|
|
||||||
const auto* tmpdir = getenv("TMPDIR");
|
|
||||||
if (tmpdir == nullptr) tmpdir = "/data/local/tmp";
|
|
||||||
if (access(tmpdir, R_OK | W_OK | X_OK) == 0) {
|
|
||||||
return tmpdir;
|
|
||||||
}
|
|
||||||
// Tests running in app context can't access /data/local/tmp,
|
|
||||||
// so try current directory if /data/local/tmp is not accessible.
|
|
||||||
return ".";
|
|
||||||
#elif defined(_WIN32)
|
|
||||||
char tmp_dir[MAX_PATH];
|
|
||||||
DWORD result = GetTempPathA(sizeof(tmp_dir), tmp_dir); // checks TMP env
|
|
||||||
CHECK_NE(result, 0ul) << "GetTempPathA failed, error: " << GetLastError();
|
|
||||||
CHECK_LT(result, sizeof(tmp_dir)) << "path truncated to: " << result;
|
|
||||||
// GetTempPath() returns a path with a trailing slash, but init()
|
|
||||||
// does not expect that, so remove it.
|
|
||||||
CHECK_EQ(tmp_dir[result - 1], '\\');
|
|
||||||
tmp_dir[result - 1] = '\0';
|
|
||||||
return tmp_dir;
|
|
||||||
#else
|
|
||||||
const auto* tmpdir = getenv("TMPDIR");
|
|
||||||
if (tmpdir == nullptr) tmpdir = "/tmp";
|
|
||||||
return tmpdir;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
TemporaryFile::TemporaryFile() {
|
|
||||||
init(GetSystemTempDir());
|
|
||||||
}
|
|
||||||
TemporaryFile::TemporaryFile(const std::string& tmp_dir) {
|
|
||||||
init(tmp_dir);
|
|
||||||
}
|
|
||||||
TemporaryFile::~TemporaryFile() {
|
|
||||||
if (fd != -1) {
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
if (remove_file_) {
|
|
||||||
unlink(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int TemporaryFile::release() {
|
|
||||||
int result = fd;
|
|
||||||
fd = -1;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
void TemporaryFile::init(const std::string& tmp_dir) {
|
|
||||||
snprintf(path, sizeof(path), "%s%cTemporaryFile-XXXXXX", tmp_dir.c_str(), OS_PATH_SEPARATOR);
|
|
||||||
fd = mkstemp(path);
|
|
||||||
}
|
|
||||||
TemporaryDir::TemporaryDir() {
|
|
||||||
init(GetSystemTempDir());
|
|
||||||
}
|
|
||||||
TemporaryDir::~TemporaryDir() {
|
|
||||||
if (!remove_dir_and_contents_) return;
|
|
||||||
auto callback = [](const char* child, const struct stat*, int file_type, struct FTW*) -> int {
|
|
||||||
switch (file_type) {
|
|
||||||
case FTW_D:
|
|
||||||
case FTW_DP:
|
|
||||||
case FTW_DNR:
|
|
||||||
if (rmdir(child) == -1) {
|
|
||||||
PLOG(ERROR) << "rmdir " << child;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case FTW_NS:
|
|
||||||
default:
|
|
||||||
if (rmdir(child) != -1) break;
|
|
||||||
// FALLTHRU (for gcc, lint, pcc, etc; and following for clang)
|
|
||||||
FALLTHROUGH_INTENDED;
|
|
||||||
case FTW_F:
|
|
||||||
case FTW_SL:
|
|
||||||
case FTW_SLN:
|
|
||||||
if (unlink(child) == -1) {
|
|
||||||
PLOG(ERROR) << "unlink " << child;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
};
|
|
||||||
nftw(path, callback, 128, FTW_DEPTH | FTW_MOUNT | FTW_PHYS);
|
|
||||||
}
|
|
||||||
bool TemporaryDir::init(const std::string& tmp_dir) {
|
|
||||||
snprintf(path, sizeof(path), "%s%cTemporaryDir-XXXXXX", tmp_dir.c_str(), OS_PATH_SEPARATOR);
|
|
||||||
return (mkdtemp(path) != nullptr);
|
|
||||||
}
|
|
||||||
namespace android {
|
|
||||||
namespace base {
|
|
||||||
// Versions of standard library APIs that support UTF-8 strings.
|
|
||||||
using namespace android::base::utf8;
|
|
||||||
bool ReadFdToString(borrowed_fd fd, std::string* content) {
|
|
||||||
content->clear();
|
|
||||||
// Although original we had small files in mind, this code gets used for
|
|
||||||
// very large files too, where the std::string growth heuristics might not
|
|
||||||
// be suitable. https://code.google.com/p/android/issues/detail?id=258500.
|
|
||||||
struct stat sb;
|
|
||||||
if (fstat(fd.get(), &sb) != -1 && sb.st_size > 0) {
|
|
||||||
content->reserve(sb.st_size);
|
|
||||||
}
|
|
||||||
char buf[BUFSIZ];
|
|
||||||
ssize_t n;
|
|
||||||
while ((n = TEMP_FAILURE_RETRY(read(fd.get(), &buf[0], sizeof(buf)))) > 0) {
|
|
||||||
content->append(buf, n);
|
|
||||||
}
|
|
||||||
return (n == 0) ? true : false;
|
|
||||||
}
|
|
||||||
bool ReadFileToString(const std::string& path, std::string* content, bool follow_symlinks) {
|
|
||||||
content->clear();
|
|
||||||
int flags = O_RDONLY | O_CLOEXEC | O_BINARY | (follow_symlinks ? 0 : O_NOFOLLOW);
|
|
||||||
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags)));
|
|
||||||
if (fd == -1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return ReadFdToString(fd, content);
|
|
||||||
}
|
|
||||||
bool WriteStringToFd(const std::string& content, borrowed_fd fd) {
|
|
||||||
const char* p = content.data();
|
|
||||||
size_t left = content.size();
|
|
||||||
while (left > 0) {
|
|
||||||
ssize_t n = TEMP_FAILURE_RETRY(write(fd.get(), p, left));
|
|
||||||
if (n == -1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
p += n;
|
|
||||||
left -= n;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
static bool CleanUpAfterFailedWrite(const std::string& path) {
|
|
||||||
// Something went wrong. Let's not leave a corrupt file lying around.
|
|
||||||
int saved_errno = errno;
|
|
||||||
unlink(path.c_str());
|
|
||||||
errno = saved_errno;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#if !defined(_WIN32)
|
|
||||||
bool WriteStringToFile(const std::string& content, const std::string& path,
|
|
||||||
mode_t mode, uid_t owner, gid_t group,
|
|
||||||
bool follow_symlinks) {
|
|
||||||
int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY |
|
|
||||||
(follow_symlinks ? 0 : O_NOFOLLOW);
|
|
||||||
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode)));
|
|
||||||
if (fd == -1) {
|
|
||||||
PLOG(ERROR) << "android::WriteStringToFile open failed";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// We do an explicit fchmod here because we assume that the caller really
|
|
||||||
// meant what they said and doesn't want the umask-influenced mode.
|
|
||||||
if (fchmod(fd, mode) == -1) {
|
|
||||||
PLOG(ERROR) << "android::WriteStringToFile fchmod failed";
|
|
||||||
return CleanUpAfterFailedWrite(path);
|
|
||||||
}
|
|
||||||
if (fchown(fd, owner, group) == -1) {
|
|
||||||
PLOG(ERROR) << "android::WriteStringToFile fchown failed";
|
|
||||||
return CleanUpAfterFailedWrite(path);
|
|
||||||
}
|
|
||||||
if (!WriteStringToFd(content, fd)) {
|
|
||||||
PLOG(ERROR) << "android::WriteStringToFile write failed";
|
|
||||||
return CleanUpAfterFailedWrite(path);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
bool WriteStringToFile(const std::string& content, const std::string& path,
|
|
||||||
bool follow_symlinks) {
|
|
||||||
int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY |
|
|
||||||
(follow_symlinks ? 0 : O_NOFOLLOW);
|
|
||||||
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags, 0666)));
|
|
||||||
if (fd == -1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return WriteStringToFd(content, fd) || CleanUpAfterFailedWrite(path);
|
|
||||||
}
|
|
||||||
bool ReadFully(borrowed_fd fd, void* data, size_t byte_count) {
|
|
||||||
uint8_t* p = reinterpret_cast<uint8_t*>(data);
|
|
||||||
size_t remaining = byte_count;
|
|
||||||
while (remaining > 0) {
|
|
||||||
ssize_t n = TEMP_FAILURE_RETRY(read(fd.get(), p, remaining));
|
|
||||||
if (n <= 0) return false;
|
|
||||||
p += n;
|
|
||||||
remaining -= n;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#if defined(_WIN32)
|
|
||||||
// Windows implementation of pread. Note that this DOES move the file descriptors read position,
|
|
||||||
// but it does so atomically.
|
|
||||||
static ssize_t pread(borrowed_fd fd, void* data, size_t byte_count, off64_t offset) {
|
|
||||||
DWORD bytes_read;
|
|
||||||
OVERLAPPED overlapped;
|
|
||||||
memset(&overlapped, 0, sizeof(OVERLAPPED));
|
|
||||||
overlapped.Offset = static_cast<DWORD>(offset);
|
|
||||||
overlapped.OffsetHigh = static_cast<DWORD>(offset >> 32);
|
|
||||||
if (!ReadFile(reinterpret_cast<HANDLE>(_get_osfhandle(fd.get())), data,
|
|
||||||
static_cast<DWORD>(byte_count), &bytes_read, &overlapped)) {
|
|
||||||
// In case someone tries to read errno (since this is masquerading as a POSIX call)
|
|
||||||
errno = EIO;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return static_cast<ssize_t>(bytes_read);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
bool ReadFullyAtOffset(borrowed_fd fd, void* data, size_t byte_count, off64_t offset) {
|
|
||||||
uint8_t* p = reinterpret_cast<uint8_t*>(data);
|
|
||||||
while (byte_count > 0) {
|
|
||||||
ssize_t n = TEMP_FAILURE_RETRY(pread(fd.get(), p, byte_count, offset));
|
|
||||||
if (n <= 0) return false;
|
|
||||||
p += n;
|
|
||||||
byte_count -= n;
|
|
||||||
offset += n;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool WriteFully(borrowed_fd fd, const void* data, size_t byte_count) {
|
|
||||||
const uint8_t* p = reinterpret_cast<const uint8_t*>(data);
|
|
||||||
size_t remaining = byte_count;
|
|
||||||
while (remaining > 0) {
|
|
||||||
ssize_t n = TEMP_FAILURE_RETRY(write(fd.get(), p, remaining));
|
|
||||||
if (n == -1) return false;
|
|
||||||
p += n;
|
|
||||||
remaining -= n;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool RemoveFileIfExists(const std::string& path, std::string* err) {
|
|
||||||
struct stat st;
|
|
||||||
#if defined(_WIN32)
|
|
||||||
// TODO: Windows version can't handle symbolic links correctly.
|
|
||||||
int result = stat(path.c_str(), &st);
|
|
||||||
bool file_type_removable = (result == 0 && S_ISREG(st.st_mode));
|
|
||||||
#else
|
|
||||||
int result = lstat(path.c_str(), &st);
|
|
||||||
bool file_type_removable = (result == 0 && (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)));
|
|
||||||
#endif
|
|
||||||
if (result == -1) {
|
|
||||||
if (errno == ENOENT || errno == ENOTDIR) return true;
|
|
||||||
if (err != nullptr) *err = strerror(errno);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (result == 0) {
|
|
||||||
if (!file_type_removable) {
|
|
||||||
if (err != nullptr) {
|
|
||||||
*err = "is not a regular file or symbolic link";
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (unlink(path.c_str()) == -1) {
|
|
||||||
if (err != nullptr) {
|
|
||||||
*err = strerror(errno);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#if !defined(_WIN32)
|
|
||||||
bool Readlink(const std::string& path, std::string* result) {
|
|
||||||
result->clear();
|
|
||||||
// Most Linux file systems (ext2 and ext4, say) limit symbolic links to
|
|
||||||
// 4095 bytes. Since we'll copy out into the string anyway, it doesn't
|
|
||||||
// waste memory to just start there. We add 1 so that we can recognize
|
|
||||||
// whether it actually fit (rather than being truncated to 4095).
|
|
||||||
std::vector<char> buf(4095 + 1);
|
|
||||||
while (true) {
|
|
||||||
ssize_t size = readlink(path.c_str(), &buf[0], buf.size());
|
|
||||||
// Unrecoverable error?
|
|
||||||
if (size == -1) return false;
|
|
||||||
// It fit! (If size == buf.size(), it may have been truncated.)
|
|
||||||
if (static_cast<size_t>(size) < buf.size()) {
|
|
||||||
result->assign(&buf[0], size);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// Double our buffer and try again.
|
|
||||||
buf.resize(buf.size() * 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#if !defined(_WIN32)
|
|
||||||
bool Realpath(const std::string& path, std::string* result) {
|
|
||||||
result->clear();
|
|
||||||
// realpath may exit with EINTR. Retry if so.
|
|
||||||
char* realpath_buf = nullptr;
|
|
||||||
do {
|
|
||||||
realpath_buf = realpath(path.c_str(), nullptr);
|
|
||||||
} while (realpath_buf == nullptr && errno == EINTR);
|
|
||||||
if (realpath_buf == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
result->assign(realpath_buf);
|
|
||||||
free(realpath_buf);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
std::string GetExecutablePath() {
|
|
||||||
#if defined(__linux__)
|
|
||||||
std::string path;
|
|
||||||
android::base::Readlink("/proc/self/exe", &path);
|
|
||||||
return path;
|
|
||||||
#elif defined(__APPLE__)
|
|
||||||
char path[PATH_MAX + 1];
|
|
||||||
uint32_t path_len = sizeof(path);
|
|
||||||
int rc = _NSGetExecutablePath(path, &path_len);
|
|
||||||
if (rc < 0) {
|
|
||||||
std::unique_ptr<char> path_buf(new char[path_len]);
|
|
||||||
_NSGetExecutablePath(path_buf.get(), &path_len);
|
|
||||||
return path_buf.get();
|
|
||||||
}
|
|
||||||
return path;
|
|
||||||
#elif defined(_WIN32)
|
|
||||||
char path[PATH_MAX + 1];
|
|
||||||
DWORD result = GetModuleFileName(NULL, path, sizeof(path) - 1);
|
|
||||||
if (result == 0 || result == sizeof(path) - 1) return "";
|
|
||||||
path[PATH_MAX - 1] = 0;
|
|
||||||
return path;
|
|
||||||
#else
|
|
||||||
#error unknown OS
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
std::string GetExecutableDirectory() {
|
|
||||||
return Dirname(GetExecutablePath());
|
|
||||||
}
|
|
||||||
std::string Basename(const std::string& path) {
|
|
||||||
// Copy path because basename may modify the string passed in.
|
|
||||||
std::string result(path);
|
|
||||||
#if !defined(__BIONIC__)
|
|
||||||
// Use lock because basename() may write to a process global and return a
|
|
||||||
// pointer to that. Note that this locking strategy only works if all other
|
|
||||||
// callers to basename in the process also grab this same lock, but its
|
|
||||||
// better than nothing. Bionic's basename returns a thread-local buffer.
|
|
||||||
static std::mutex& basename_lock = *new std::mutex();
|
|
||||||
std::lock_guard<std::mutex> lock(basename_lock);
|
|
||||||
#endif
|
|
||||||
// Note that if std::string uses copy-on-write strings, &str[0] will cause
|
|
||||||
// the copy to be made, so there is no chance of us accidentally writing to
|
|
||||||
// the storage for 'path'.
|
|
||||||
char* name = basename(&result[0]);
|
|
||||||
// In case basename returned a pointer to a process global, copy that string
|
|
||||||
// before leaving the lock.
|
|
||||||
result.assign(name);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
std::string Dirname(const std::string& path) {
|
|
||||||
// Copy path because dirname may modify the string passed in.
|
|
||||||
std::string result(path);
|
|
||||||
#if !defined(__BIONIC__)
|
|
||||||
// Use lock because dirname() may write to a process global and return a
|
|
||||||
// pointer to that. Note that this locking strategy only works if all other
|
|
||||||
// callers to dirname in the process also grab this same lock, but its
|
|
||||||
// better than nothing. Bionic's dirname returns a thread-local buffer.
|
|
||||||
static std::mutex& dirname_lock = *new std::mutex();
|
|
||||||
std::lock_guard<std::mutex> lock(dirname_lock);
|
|
||||||
#endif
|
|
||||||
// Note that if std::string uses copy-on-write strings, &str[0] will cause
|
|
||||||
// the copy to be made, so there is no chance of us accidentally writing to
|
|
||||||
// the storage for 'path'.
|
|
||||||
char* parent = dirname(&result[0]);
|
|
||||||
// In case dirname returned a pointer to a process global, copy that string
|
|
||||||
// before leaving the lock.
|
|
||||||
result.assign(parent);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
} // namespace base
|
|
||||||
} // namespace android
|
|
||||||
|
|
@ -1,96 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2015 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <string>
|
|
||||||
#include "android-base/macros.h"
|
|
||||||
#include "android-base/off64_t.h"
|
|
||||||
#include "android-base/unique_fd.h"
|
|
||||||
#if !defined(_WIN32) && !defined(O_BINARY)
|
|
||||||
/** Windows needs O_BINARY, but Unix never mangles line endings. */
|
|
||||||
#define O_BINARY 0
|
|
||||||
#endif
|
|
||||||
#if defined(_WIN32) && !defined(O_CLOEXEC)
|
|
||||||
/** Windows has O_CLOEXEC but calls it O_NOINHERIT for some reason. */
|
|
||||||
#define O_CLOEXEC O_NOINHERIT
|
|
||||||
#endif
|
|
||||||
class TemporaryFile {
|
|
||||||
public:
|
|
||||||
TemporaryFile();
|
|
||||||
explicit TemporaryFile(const std::string& tmp_dir);
|
|
||||||
~TemporaryFile();
|
|
||||||
// Release the ownership of fd, caller is reponsible for closing the
|
|
||||||
// fd or stream properly.
|
|
||||||
int release();
|
|
||||||
// Don't remove the temporary file in the destructor.
|
|
||||||
void DoNotRemove() { remove_file_ = false; }
|
|
||||||
int fd;
|
|
||||||
char path[1024];
|
|
||||||
private:
|
|
||||||
void init(const std::string& tmp_dir);
|
|
||||||
bool remove_file_ = true;
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(TemporaryFile);
|
|
||||||
};
|
|
||||||
class TemporaryDir {
|
|
||||||
public:
|
|
||||||
TemporaryDir();
|
|
||||||
~TemporaryDir();
|
|
||||||
// Don't remove the temporary dir in the destructor.
|
|
||||||
void DoNotRemove() { remove_dir_and_contents_ = false; }
|
|
||||||
char path[1024];
|
|
||||||
private:
|
|
||||||
bool init(const std::string& tmp_dir);
|
|
||||||
bool remove_dir_and_contents_ = true;
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(TemporaryDir);
|
|
||||||
};
|
|
||||||
namespace android {
|
|
||||||
namespace base {
|
|
||||||
bool ReadFdToString(borrowed_fd fd, std::string* content);
|
|
||||||
bool ReadFileToString(const std::string& path, std::string* content,
|
|
||||||
bool follow_symlinks = false);
|
|
||||||
bool WriteStringToFile(const std::string& content, const std::string& path,
|
|
||||||
bool follow_symlinks = false);
|
|
||||||
bool WriteStringToFd(const std::string& content, borrowed_fd fd);
|
|
||||||
#if !defined(_WIN32)
|
|
||||||
bool WriteStringToFile(const std::string& content, const std::string& path,
|
|
||||||
mode_t mode, uid_t owner, gid_t group,
|
|
||||||
bool follow_symlinks = false);
|
|
||||||
#endif
|
|
||||||
bool ReadFully(borrowed_fd fd, void* data, size_t byte_count);
|
|
||||||
// Reads `byte_count` bytes from the file descriptor at the specified offset.
|
|
||||||
// Returns false if there was an IO error or EOF was reached before reading `byte_count` bytes.
|
|
||||||
//
|
|
||||||
// NOTE: On Linux/Mac, this function wraps pread, which provides atomic read support without
|
|
||||||
// modifying the read pointer of the file descriptor. On Windows, however, the read pointer does
|
|
||||||
// get modified. This means that ReadFullyAtOffset can be used concurrently with other calls to the
|
|
||||||
// same function, but concurrently seeking or reading incrementally can lead to unexpected
|
|
||||||
// behavior.
|
|
||||||
bool ReadFullyAtOffset(borrowed_fd fd, void* data, size_t byte_count, off64_t offset);
|
|
||||||
bool WriteFully(borrowed_fd fd, const void* data, size_t byte_count);
|
|
||||||
bool RemoveFileIfExists(const std::string& path, std::string* err = nullptr);
|
|
||||||
#if !defined(_WIN32)
|
|
||||||
bool Realpath(const std::string& path, std::string* result);
|
|
||||||
bool Readlink(const std::string& path, std::string* result);
|
|
||||||
#endif
|
|
||||||
std::string GetExecutablePath();
|
|
||||||
std::string GetExecutableDirectory();
|
|
||||||
// Like the regular basename and dirname, but thread-safe on all
|
|
||||||
// platforms and capable of correctly handling exotic Windows paths.
|
|
||||||
std::string Basename(const std::string& path);
|
|
||||||
std::string Dirname(const std::string& path);
|
|
||||||
} // namespace base
|
|
||||||
} // namespace android
|
|
||||||
|
|
@ -1,423 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2015 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
#if defined(_WIN32)
|
|
||||||
#include <windows.h>
|
|
||||||
#endif
|
|
||||||
#include "android-base/logging.h"
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <libgen.h>
|
|
||||||
#include <time.h>
|
|
||||||
// For getprogname(3) or program_invocation_short_name.
|
|
||||||
#if defined(__ANDROID__) || defined(__APPLE__)
|
|
||||||
#include <stdlib.h>
|
|
||||||
#elif defined(__GLIBC__)
|
|
||||||
#include <errno.h>
|
|
||||||
#endif
|
|
||||||
#if defined(__linux__)
|
|
||||||
#include <sys/uio.h>
|
|
||||||
#endif
|
|
||||||
#include <iostream>
|
|
||||||
#include <limits>
|
|
||||||
#include <mutex>
|
|
||||||
#include <sstream>
|
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
// Headers for LogMessage::LogLine.
|
|
||||||
#ifdef __ANDROID__
|
|
||||||
#include <android/log.h>
|
|
||||||
#include <android/set_abort_message.h>
|
|
||||||
#else
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#endif
|
|
||||||
#include <android-base/file.h>
|
|
||||||
#include <android-base/macros.h>
|
|
||||||
#include <android-base/parseint.h>
|
|
||||||
#include <android-base/strings.h>
|
|
||||||
#include <android-base/threads.h>
|
|
||||||
namespace android {
|
|
||||||
namespace base {
|
|
||||||
// BSD-based systems like Android/macOS have getprogname(). Others need us to provide one.
|
|
||||||
#if defined(__GLIBC__) || defined(_WIN32)
|
|
||||||
static const char* getprogname() {
|
|
||||||
#if defined(__GLIBC__)
|
|
||||||
return program_invocation_short_name;
|
|
||||||
#elif defined(_WIN32)
|
|
||||||
static bool first = true;
|
|
||||||
static char progname[MAX_PATH] = {};
|
|
||||||
if (first) {
|
|
||||||
snprintf(progname, sizeof(progname), "%s",
|
|
||||||
android::base::Basename(android::base::GetExecutablePath()).c_str());
|
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
return progname;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
static const char* GetFileBasename(const char* file) {
|
|
||||||
// We can't use basename(3) even on Unix because the Mac doesn't
|
|
||||||
// have a non-modifying basename.
|
|
||||||
const char* last_slash = strrchr(file, '/');
|
|
||||||
if (last_slash != nullptr) {
|
|
||||||
return last_slash + 1;
|
|
||||||
}
|
|
||||||
#if defined(_WIN32)
|
|
||||||
const char* last_backslash = strrchr(file, '\\');
|
|
||||||
if (last_backslash != nullptr) {
|
|
||||||
return last_backslash + 1;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
#if defined(__linux__)
|
|
||||||
static int OpenKmsg() {
|
|
||||||
#if defined(__ANDROID__)
|
|
||||||
// pick up 'file w /dev/kmsg' environment from daemon's init rc file
|
|
||||||
const auto val = getenv("ANDROID_FILE__dev_kmsg");
|
|
||||||
if (val != nullptr) {
|
|
||||||
int fd;
|
|
||||||
if (android::base::ParseInt(val, &fd, 0)) {
|
|
||||||
auto flags = fcntl(fd, F_GETFL);
|
|
||||||
if ((flags != -1) && ((flags & O_ACCMODE) == O_WRONLY)) return fd;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return TEMP_FAILURE_RETRY(open("/dev/kmsg", O_WRONLY | O_CLOEXEC));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
static std::mutex& LoggingLock() {
|
|
||||||
static auto& logging_lock = *new std::mutex();
|
|
||||||
return logging_lock;
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
static std::recursive_mutex& TagLock() {
|
|
||||||
static auto& tag_lock = *new std::recursive_mutex();
|
|
||||||
return tag_lock;
|
|
||||||
}
|
|
||||||
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 bool gInitialized = false;
|
|
||||||
static LogSeverity gMinimumLogSeverity = INFO;
|
|
||||||
#if defined(__linux__)
|
|
||||||
void KernelLogger(android::base::LogId, android::base::LogSeverity severity,
|
|
||||||
const char* tag, const char*, unsigned int, const char* msg) {
|
|
||||||
// clang-format off
|
|
||||||
static constexpr int kLogSeverityToKernelLogLevel[] = {
|
|
||||||
[android::base::VERBOSE] = 7, // KERN_DEBUG (there is no verbose kernel log
|
|
||||||
// level)
|
|
||||||
[android::base::DEBUG] = 7, // KERN_DEBUG
|
|
||||||
[android::base::INFO] = 6, // KERN_INFO
|
|
||||||
[android::base::WARNING] = 4, // KERN_WARNING
|
|
||||||
[android::base::ERROR] = 3, // KERN_ERROR
|
|
||||||
[android::base::FATAL_WITHOUT_ABORT] = 2, // KERN_CRIT
|
|
||||||
[android::base::FATAL] = 2, // KERN_CRIT
|
|
||||||
};
|
|
||||||
// clang-format on
|
|
||||||
static_assert(arraysize(kLogSeverityToKernelLogLevel) == android::base::FATAL + 1,
|
|
||||||
"Mismatch in size of kLogSeverityToKernelLogLevel and values in LogSeverity");
|
|
||||||
static int klog_fd = OpenKmsg();
|
|
||||||
if (klog_fd == -1) return;
|
|
||||||
int level = kLogSeverityToKernelLogLevel[severity];
|
|
||||||
// The kernel's printk buffer is only 1024 bytes.
|
|
||||||
// TODO: should we automatically break up long lines into multiple lines?
|
|
||||||
// Or we could log but with something like "..." at the end?
|
|
||||||
char buf[1024];
|
|
||||||
size_t size = snprintf(buf, sizeof(buf), "<%d>%s: %s\n", level, tag, msg);
|
|
||||||
if (size > sizeof(buf)) {
|
|
||||||
size = snprintf(buf, sizeof(buf), "<%d>%s: %zu-byte message too long for printk\n",
|
|
||||||
level, tag, size);
|
|
||||||
}
|
|
||||||
iovec iov[1];
|
|
||||||
iov[0].iov_base = buf;
|
|
||||||
iov[0].iov_len = size;
|
|
||||||
TEMP_FAILURE_RETRY(writev(klog_fd, iov, 1));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
void StderrLogger(LogId, LogSeverity severity, const char* tag, const char* file, unsigned int line,
|
|
||||||
const char* message) {
|
|
||||||
struct tm now;
|
|
||||||
time_t t = time(nullptr);
|
|
||||||
#if defined(_WIN32)
|
|
||||||
localtime_s(&now, &t);
|
|
||||||
#else
|
|
||||||
localtime_r(&t, &now);
|
|
||||||
#endif
|
|
||||||
char timestamp[32];
|
|
||||||
strftime(timestamp, sizeof(timestamp), "%m-%d %H:%M:%S", &now);
|
|
||||||
static const char log_characters[] = "VDIWEFF";
|
|
||||||
static_assert(arraysize(log_characters) - 1 == FATAL + 1,
|
|
||||||
"Mismatch in size of log_characters and values in LogSeverity");
|
|
||||||
char severity_char = log_characters[severity];
|
|
||||||
fprintf(stderr, "%s %c %s %5d %5" PRIu64 " %s:%u] %s\n", tag ? tag : "nullptr", severity_char,
|
|
||||||
timestamp, getpid(), GetThreadId(), file, line, message);
|
|
||||||
}
|
|
||||||
void StdioLogger(LogId, LogSeverity severity, const char* /*tag*/, const char* /*file*/,
|
|
||||||
unsigned int /*line*/, const char* message) {
|
|
||||||
if (severity >= WARNING) {
|
|
||||||
fflush(stdout);
|
|
||||||
fprintf(stderr, "%s: %s\n", GetFileBasename(getprogname()), message);
|
|
||||||
} else {
|
|
||||||
fprintf(stdout, "%s\n", message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void DefaultAborter(const char* abort_message) {
|
|
||||||
#ifdef __ANDROID__
|
|
||||||
android_set_abort_message(abort_message);
|
|
||||||
#else
|
|
||||||
UNUSED(abort_message);
|
|
||||||
#endif
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
#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];
|
|
||||||
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];
|
|
||||||
if (priority == ANDROID_LOG_FATAL) {
|
|
||||||
__android_log_buf_print(lg_id, priority, tag, "%s:%u] %s", file, line,
|
|
||||||
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));
|
|
||||||
SetAborter(std::forward<AbortFunction>(aborter));
|
|
||||||
if (gInitialized) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
gInitialized = true;
|
|
||||||
// Stash the command line for later use. We can use /proc/self/cmdline on
|
|
||||||
// Linux to recover this, but we don't have that luxury on the Mac/Windows,
|
|
||||||
// and there are a couple of argv[0] variants that are commonly used.
|
|
||||||
if (argv != nullptr) {
|
|
||||||
SetDefaultTag(basename(argv[0]));
|
|
||||||
}
|
|
||||||
const char* tags = getenv("ANDROID_LOG_TAGS");
|
|
||||||
if (tags == nullptr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
std::vector<std::string> specs = Split(tags, " ");
|
|
||||||
for (size_t i = 0; i < specs.size(); ++i) {
|
|
||||||
// "tag-pattern:[vdiwefs]"
|
|
||||||
std::string spec(specs[i]);
|
|
||||||
if (spec.size() == 3 && StartsWith(spec, "*:")) {
|
|
||||||
switch (spec[2]) {
|
|
||||||
case 'v':
|
|
||||||
gMinimumLogSeverity = VERBOSE;
|
|
||||||
continue;
|
|
||||||
case 'd':
|
|
||||||
gMinimumLogSeverity = DEBUG;
|
|
||||||
continue;
|
|
||||||
case 'i':
|
|
||||||
gMinimumLogSeverity = INFO;
|
|
||||||
continue;
|
|
||||||
case 'w':
|
|
||||||
gMinimumLogSeverity = WARNING;
|
|
||||||
continue;
|
|
||||||
case 'e':
|
|
||||||
gMinimumLogSeverity = ERROR;
|
|
||||||
continue;
|
|
||||||
case 'f':
|
|
||||||
gMinimumLogSeverity = 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;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LOG(FATAL) << "unsupported '" << spec << "' in ANDROID_LOG_TAGS (" << tags
|
|
||||||
<< ")";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void SetLogger(LogFunction&& logger) {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
// 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)
|
|
||||||
: file_(GetFileBasename(file)),
|
|
||||||
line_number_(line),
|
|
||||||
id_(id),
|
|
||||||
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_; }
|
|
||||||
LogId GetId() const {
|
|
||||||
return id_;
|
|
||||||
}
|
|
||||||
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 LogId id_;
|
|
||||||
const LogSeverity severity_;
|
|
||||||
const char* const tag_;
|
|
||||||
const int error_;
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(LogMessageData);
|
|
||||||
};
|
|
||||||
LogMessage::LogMessage(const char* file, unsigned int line, LogId id, LogSeverity severity,
|
|
||||||
const char* tag, int error)
|
|
||||||
: data_(new LogMessageData(file, line, id, severity, tag, error)) {}
|
|
||||||
LogMessage::~LogMessage() {
|
|
||||||
// Check severity again. This is duplicate work wrt/ LOG macros, but not LOG_STREAM.
|
|
||||||
if (!WOULD_LOG(data_->GetSeverity())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Finish constructing the message.
|
|
||||||
if (data_->GetError() != -1) {
|
|
||||||
data_->GetBuffer() << ": " << strerror(data_->GetError());
|
|
||||||
}
|
|
||||||
std::string msg(data_->ToString());
|
|
||||||
if (data_->GetSeverity() == FATAL) {
|
|
||||||
#ifdef __ANDROID__
|
|
||||||
// Set the bionic abort message early to avoid liblog doing it
|
|
||||||
// with the individual lines, so that we get the whole message.
|
|
||||||
android_set_abort_message(msg.c_str());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// Do the actual logging with the lock held.
|
|
||||||
std::lock_guard<std::mutex> lock(LoggingLock());
|
|
||||||
if (msg.find('\n') == std::string::npos) {
|
|
||||||
LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetId(), 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]);
|
|
||||||
// Undo the zero-termination so we can give the complete message to the aborter.
|
|
||||||
msg[nl] = '\n';
|
|
||||||
i = nl + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Abort if necessary.
|
|
||||||
if (data_->GetSeverity() == FATAL) {
|
|
||||||
Aborter()(msg.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::ostream& LogMessage::stream() {
|
|
||||||
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);
|
|
||||||
} else {
|
|
||||||
Logger()(id, severity, tag, file, line, message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LogSeverity GetMinimumLogSeverity() {
|
|
||||||
return gMinimumLogSeverity;
|
|
||||||
}
|
|
||||||
LogSeverity SetMinimumLogSeverity(LogSeverity new_severity) {
|
|
||||||
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
|
|
||||||
|
|
@ -1,429 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2015 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
//
|
|
||||||
// Google-style C++ logging.
|
|
||||||
//
|
|
||||||
// This header provides a C++ stream interface to logging.
|
|
||||||
//
|
|
||||||
// To log:
|
|
||||||
//
|
|
||||||
// LOG(INFO) << "Some text; " << some_value;
|
|
||||||
//
|
|
||||||
// Replace `INFO` with any severity from `enum LogSeverity`.
|
|
||||||
//
|
|
||||||
// To log the result of a failed function and include the string
|
|
||||||
// representation of `errno` at the end:
|
|
||||||
//
|
|
||||||
// PLOG(ERROR) << "Write failed";
|
|
||||||
//
|
|
||||||
// The output will be something like `Write failed: I/O error`.
|
|
||||||
// Remember this as 'P' as in perror(3).
|
|
||||||
//
|
|
||||||
// To output your own types, simply implement operator<< as normal.
|
|
||||||
//
|
|
||||||
// By default, output goes to logcat on Android and stderr on the host.
|
|
||||||
// A process can use `SetLogger` to decide where all logging goes.
|
|
||||||
// Implementations are provided for logcat, stderr, and dmesg.
|
|
||||||
//
|
|
||||||
// By default, the process' name is used as the log tag.
|
|
||||||
// Code can choose a specific log tag by defining LOG_TAG
|
|
||||||
// before including this header.
|
|
||||||
// This header also provides assertions:
|
|
||||||
//
|
|
||||||
// CHECK(must_be_true);
|
|
||||||
// CHECK_EQ(a, b) << z_is_interesting_too;
|
|
||||||
// NOTE: For Windows, you must include logging.h after windows.h to allow the
|
|
||||||
// following code to suppress the evil ERROR macro:
|
|
||||||
#ifdef _WIN32
|
|
||||||
// windows.h includes wingdi.h which defines an evil macro ERROR.
|
|
||||||
#ifdef ERROR
|
|
||||||
#undef ERROR
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#include <functional>
|
|
||||||
#include <memory>
|
|
||||||
#include <ostream>
|
|
||||||
#include "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 "LSPosed"
|
|
||||||
#endif
|
|
||||||
namespace android {
|
|
||||||
namespace base {
|
|
||||||
enum LogSeverity {
|
|
||||||
VERBOSE,
|
|
||||||
DEBUG,
|
|
||||||
INFO,
|
|
||||||
WARNING,
|
|
||||||
ERROR,
|
|
||||||
FATAL_WITHOUT_ABORT,
|
|
||||||
FATAL,
|
|
||||||
};
|
|
||||||
enum LogId {
|
|
||||||
DEFAULT,
|
|
||||||
MAIN,
|
|
||||||
SYSTEM,
|
|
||||||
};
|
|
||||||
using LogFunction = std::function<void(LogId, LogSeverity, const char*, const char*,
|
|
||||||
unsigned int, const char*)>;
|
|
||||||
using AbortFunction = std::function<void(const char*)>;
|
|
||||||
// Loggers for use with InitLogging/SetLogger.
|
|
||||||
// Log to the kernel log (dmesg).
|
|
||||||
void KernelLogger(LogId, LogSeverity, const char*, const char*, unsigned int, const char*);
|
|
||||||
// Log to stderr in the full logcat format (with pid/tid/time/tag details).
|
|
||||||
void StderrLogger(LogId, LogSeverity, const char*, const char*, unsigned int, const char*);
|
|
||||||
// Log just the message to stdout/stderr (without pid/tid/time/tag details).
|
|
||||||
// The choice of stdout versus stderr is based on the severity.
|
|
||||||
// Errors are also prefixed by the program name (as with err(3)/error(3)).
|
|
||||||
// Useful for replacing printf(3)/perror(3)/err(3)/error(3) in command-line tools.
|
|
||||||
void StdioLogger(LogId, LogSeverity, const char*, const char*, unsigned int, const char*);
|
|
||||||
void DefaultAborter(const char* abort_message);
|
|
||||||
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 {
|
|
||||||
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_;
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
// Configure logging based on ANDROID_LOG_TAGS environment variable.
|
|
||||||
// We need to parse a string that looks like
|
|
||||||
//
|
|
||||||
// *:v jdwp:d dalvikvm:d dalvikvm-gc:i dalvikvmi:i
|
|
||||||
//
|
|
||||||
// The tag (or '*' for the global level) comes first, followed by a colon and a
|
|
||||||
// letter indicating the minimum priority level we're expected to log. This can
|
|
||||||
// be used to reveal or conceal logs with specific tags.
|
|
||||||
#ifdef __ANDROID__
|
|
||||||
#define INIT_LOGGING_DEFAULT_LOGGER LogdLogger()
|
|
||||||
#else
|
|
||||||
#define INIT_LOGGING_DEFAULT_LOGGER StderrLogger
|
|
||||||
#endif
|
|
||||||
void InitLogging(char* argv[],
|
|
||||||
LogFunction&& logger = INIT_LOGGING_DEFAULT_LOGGER,
|
|
||||||
AbortFunction&& aborter = DefaultAborter);
|
|
||||||
#undef INIT_LOGGING_DEFAULT_LOGGER
|
|
||||||
// Replace the current logger.
|
|
||||||
void SetLogger(LogFunction&& logger);
|
|
||||||
// Replace the current aborter.
|
|
||||||
void SetAborter(AbortFunction&& aborter);
|
|
||||||
class ErrnoRestorer {
|
|
||||||
public:
|
|
||||||
ErrnoRestorer()
|
|
||||||
: saved_errno_(errno) {
|
|
||||||
}
|
|
||||||
~ErrnoRestorer() {
|
|
||||||
errno = saved_errno_;
|
|
||||||
}
|
|
||||||
// Allow this object to be used as part of && operation.
|
|
||||||
operator bool() const {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
const int saved_errno_;
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(ErrnoRestorer);
|
|
||||||
};
|
|
||||||
// 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((SEVERITY_LAMBDA(severity)) >= ::android::base::GetMinimumLogSeverity()) || \
|
|
||||||
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) 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) \
|
|
||||||
.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)
|
|
||||||
// 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())
|
|
||||||
// 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) \
|
|
||||||
.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::DEFAULT, \
|
|
||||||
::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 \
|
|
||||||
<< ", " #RHS "=" << _values.rhs << ") "
|
|
||||||
// clang-format on
|
|
||||||
// Check whether a condition holds between x and y, LOG(FATAL) if not. The value
|
|
||||||
// of the expressions x and y is evaluated once. Extra logging can be appended
|
|
||||||
// using << after. For example:
|
|
||||||
//
|
|
||||||
// CHECK_NE(0 == 1, false) results in
|
|
||||||
// "Check failed: false != false (0==1=false, false=false) ".
|
|
||||||
#define CHECK_EQ(x, y) CHECK_OP(x, y, == )
|
|
||||||
#define CHECK_NE(x, y) CHECK_OP(x, y, != )
|
|
||||||
#define CHECK_LE(x, y) CHECK_OP(x, y, <= )
|
|
||||||
#define CHECK_LT(x, y) CHECK_OP(x, y, < )
|
|
||||||
#define CHECK_GE(x, y) CHECK_OP(x, y, >= )
|
|
||||||
#define CHECK_GT(x, y) CHECK_OP(x, y, > )
|
|
||||||
// clang-format off
|
|
||||||
// Helper for CHECK_STRxx(s1,s2) macros.
|
|
||||||
#define CHECK_STROP(s1, s2, sense) \
|
|
||||||
while (UNLIKELY((strcmp(s1, s2) == 0) != (sense))) \
|
|
||||||
ABORT_AFTER_LOG_FATAL \
|
|
||||||
::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \
|
|
||||||
::android::base::FATAL, _LOG_TAG_INTERNAL, -1) \
|
|
||||||
.stream() \
|
|
||||||
<< "Check failed: " << "\"" << (s1) << "\"" \
|
|
||||||
<< ((sense) ? " == " : " != ") << "\"" << (s2) << "\""
|
|
||||||
// clang-format on
|
|
||||||
// Check for string (const char*) equality between s1 and s2, LOG(FATAL) if not.
|
|
||||||
#define CHECK_STREQ(s1, s2) CHECK_STROP(s1, s2, true)
|
|
||||||
#define CHECK_STRNE(s1, s2) CHECK_STROP(s1, s2, false)
|
|
||||||
// Perform the pthread function call(args), LOG(FATAL) on error.
|
|
||||||
#define CHECK_PTHREAD_CALL(call, args, what) \
|
|
||||||
do { \
|
|
||||||
int rc = call args; \
|
|
||||||
if (rc != 0) { \
|
|
||||||
errno = rc; \
|
|
||||||
ABORT_AFTER_LOG_FATAL \
|
|
||||||
PLOG(FATAL) << #call << " failed for " << (what); \
|
|
||||||
} \
|
|
||||||
} while (false)
|
|
||||||
// CHECK that can be used in a constexpr function. For example:
|
|
||||||
//
|
|
||||||
// constexpr int half(int n) {
|
|
||||||
// return
|
|
||||||
// DCHECK_CONSTEXPR(n >= 0, , 0)
|
|
||||||
// CHECK_CONSTEXPR((n & 1) == 0),
|
|
||||||
// << "Extra debugging output: n = " << n, 0)
|
|
||||||
// n / 2;
|
|
||||||
// }
|
|
||||||
#define CHECK_CONSTEXPR(x, out, dummy) \
|
|
||||||
(UNLIKELY(!(x))) \
|
|
||||||
? (LOG(FATAL) << "Check failed: " << #x out, dummy) \
|
|
||||||
:
|
|
||||||
// DCHECKs are debug variants of CHECKs only enabled in debug builds. Generally
|
|
||||||
// CHECK should be used unless profiling identifies a CHECK as being in
|
|
||||||
// performance critical code.
|
|
||||||
#if defined(NDEBUG) && !defined(__clang_analyzer__)
|
|
||||||
static constexpr bool kEnableDChecks = false;
|
|
||||||
#else
|
|
||||||
static constexpr bool kEnableDChecks = true;
|
|
||||||
#endif
|
|
||||||
#define DCHECK(x) \
|
|
||||||
if (::android::base::kEnableDChecks) CHECK(x)
|
|
||||||
#define DCHECK_EQ(x, y) \
|
|
||||||
if (::android::base::kEnableDChecks) CHECK_EQ(x, y)
|
|
||||||
#define DCHECK_NE(x, y) \
|
|
||||||
if (::android::base::kEnableDChecks) CHECK_NE(x, y)
|
|
||||||
#define DCHECK_LE(x, y) \
|
|
||||||
if (::android::base::kEnableDChecks) CHECK_LE(x, y)
|
|
||||||
#define DCHECK_LT(x, y) \
|
|
||||||
if (::android::base::kEnableDChecks) CHECK_LT(x, y)
|
|
||||||
#define DCHECK_GE(x, y) \
|
|
||||||
if (::android::base::kEnableDChecks) CHECK_GE(x, y)
|
|
||||||
#define DCHECK_GT(x, y) \
|
|
||||||
if (::android::base::kEnableDChecks) CHECK_GT(x, y)
|
|
||||||
#define DCHECK_STREQ(s1, s2) \
|
|
||||||
if (::android::base::kEnableDChecks) CHECK_STREQ(s1, s2)
|
|
||||||
#define DCHECK_STRNE(s1, s2) \
|
|
||||||
if (::android::base::kEnableDChecks) CHECK_STRNE(s1, s2)
|
|
||||||
#if defined(NDEBUG) && !defined(__clang_analyzer__)
|
|
||||||
#define DCHECK_CONSTEXPR(x, out, dummy)
|
|
||||||
#else
|
|
||||||
#define DCHECK_CONSTEXPR(x, out, dummy) CHECK_CONSTEXPR(x, out, dummy)
|
|
||||||
#endif
|
|
||||||
// Temporary class created to evaluate the LHS and RHS, used with
|
|
||||||
// MakeEagerEvaluator to infer the types of LHS and RHS.
|
|
||||||
template <typename LHS, typename RHS>
|
|
||||||
struct EagerEvaluator {
|
|
||||||
constexpr EagerEvaluator(LHS l, RHS r) : lhs(l), rhs(r) {
|
|
||||||
}
|
|
||||||
LHS lhs;
|
|
||||||
RHS rhs;
|
|
||||||
};
|
|
||||||
// Helper function for CHECK_xx.
|
|
||||||
template <typename LHS, typename RHS>
|
|
||||||
constexpr EagerEvaluator<LHS, RHS> MakeEagerEvaluator(LHS lhs, RHS rhs) {
|
|
||||||
return EagerEvaluator<LHS, RHS>(lhs, rhs);
|
|
||||||
}
|
|
||||||
// Explicitly instantiate EagerEvalue for pointers so that char*s aren't treated
|
|
||||||
// as strings. To compare strings use CHECK_STREQ and CHECK_STRNE. We rely on
|
|
||||||
// signed/unsigned warnings to protect you against combinations not explicitly
|
|
||||||
// listed below.
|
|
||||||
#define EAGER_PTR_EVALUATOR(T1, T2) \
|
|
||||||
template <> \
|
|
||||||
struct EagerEvaluator<T1, T2> { \
|
|
||||||
EagerEvaluator(T1 l, T2 r) \
|
|
||||||
: lhs(reinterpret_cast<const void*>(l)), \
|
|
||||||
rhs(reinterpret_cast<const void*>(r)) { \
|
|
||||||
} \
|
|
||||||
const void* lhs; \
|
|
||||||
const void* rhs; \
|
|
||||||
}
|
|
||||||
EAGER_PTR_EVALUATOR(const char*, const char*);
|
|
||||||
EAGER_PTR_EVALUATOR(const char*, char*);
|
|
||||||
EAGER_PTR_EVALUATOR(char*, const char*);
|
|
||||||
EAGER_PTR_EVALUATOR(char*, char*);
|
|
||||||
EAGER_PTR_EVALUATOR(const unsigned char*, const unsigned char*);
|
|
||||||
EAGER_PTR_EVALUATOR(const unsigned char*, unsigned char*);
|
|
||||||
EAGER_PTR_EVALUATOR(unsigned char*, const unsigned char*);
|
|
||||||
EAGER_PTR_EVALUATOR(unsigned char*, unsigned char*);
|
|
||||||
EAGER_PTR_EVALUATOR(const signed char*, const signed char*);
|
|
||||||
EAGER_PTR_EVALUATOR(const signed char*, signed char*);
|
|
||||||
EAGER_PTR_EVALUATOR(signed char*, const signed char*);
|
|
||||||
EAGER_PTR_EVALUATOR(signed char*, signed char*);
|
|
||||||
// Data for the log message, not stored in LogMessage to avoid increasing the
|
|
||||||
// stack size.
|
|
||||||
class LogMessageData;
|
|
||||||
// A LogMessage is a temporarily scoped object used by LOG and the unlikely part
|
|
||||||
// of a CHECK. The destructor will abort if the severity is FATAL.
|
|
||||||
class LogMessage {
|
|
||||||
public:
|
|
||||||
LogMessage(const char* file, unsigned int line, LogId id, 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, LogId id, LogSeverity severity,
|
|
||||||
const char* tag, const char* msg);
|
|
||||||
private:
|
|
||||||
const std::unique_ptr<LogMessageData> data_;
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(LogMessage);
|
|
||||||
};
|
|
||||||
// Get the minimum severity level for logging.
|
|
||||||
LogSeverity GetMinimumLogSeverity();
|
|
||||||
// Set the minimum severity level for logging, returning the old severity.
|
|
||||||
LogSeverity SetMinimumLogSeverity(LogSeverity new_severity);
|
|
||||||
// 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 {
|
|
||||||
// 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.
|
|
||||||
// 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 std::ostream& operator<<(std::ostream& stream, const std::string* string_pointer)
|
|
||||||
OSTREAM_STRING_POINTER_USAGE_WARNING {
|
|
||||||
return stream << static_cast<const void*>(string_pointer);
|
|
||||||
}
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
} // namespace std
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2018 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
#if defined(__APPLE__)
|
|
||||||
/** Mac OS has always had a 64-bit off_t, so it doesn't have off64_t. */
|
|
||||||
typedef off_t off64_t;
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,122 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2015 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
#include <errno.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <limits>
|
|
||||||
#include <string>
|
|
||||||
#include <type_traits>
|
|
||||||
namespace android {
|
|
||||||
namespace base {
|
|
||||||
// Parses the unsigned decimal or hexadecimal integer in the string 's' and sets
|
|
||||||
// 'out' to that value if it is specified. Optionally allows the caller to define
|
|
||||||
// a 'max' beyond which otherwise valid values will be rejected. Returns boolean
|
|
||||||
// success; 'out' is untouched if parsing fails.
|
|
||||||
template <typename T>
|
|
||||||
bool ParseUint(const char* s, T* out, T max = std::numeric_limits<T>::max(),
|
|
||||||
bool allow_suffixes = false) {
|
|
||||||
static_assert(std::is_unsigned<T>::value, "ParseUint can only be used with unsigned types");
|
|
||||||
while (isspace(*s)) {
|
|
||||||
s++;
|
|
||||||
}
|
|
||||||
if (s[0] == '-') {
|
|
||||||
errno = EINVAL;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
int base = (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) ? 16 : 10;
|
|
||||||
errno = 0;
|
|
||||||
char* end;
|
|
||||||
unsigned long long int result = strtoull(s, &end, base);
|
|
||||||
if (errno != 0) return false;
|
|
||||||
if (end == s) {
|
|
||||||
errno = EINVAL;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (*end != '\0') {
|
|
||||||
const char* suffixes = "bkmgtpe";
|
|
||||||
const char* suffix;
|
|
||||||
if ((!allow_suffixes || (suffix = strchr(suffixes, tolower(*end))) == nullptr) ||
|
|
||||||
__builtin_mul_overflow(result, 1ULL << (10 * (suffix - suffixes)), &result)) {
|
|
||||||
errno = EINVAL;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (max < result) {
|
|
||||||
errno = ERANGE;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (out != nullptr) {
|
|
||||||
*out = static_cast<T>(result);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// TODO: string_view
|
|
||||||
template <typename T>
|
|
||||||
bool ParseUint(const std::string& s, T* out, T max = std::numeric_limits<T>::max(),
|
|
||||||
bool allow_suffixes = false) {
|
|
||||||
return ParseUint(s.c_str(), out, max, allow_suffixes);
|
|
||||||
}
|
|
||||||
template <typename T>
|
|
||||||
bool ParseByteCount(const char* s, T* out, T max = std::numeric_limits<T>::max()) {
|
|
||||||
return ParseUint(s, out, max, true);
|
|
||||||
}
|
|
||||||
// TODO: string_view
|
|
||||||
template <typename T>
|
|
||||||
bool ParseByteCount(const std::string& s, T* out, T max = std::numeric_limits<T>::max()) {
|
|
||||||
return ParseByteCount(s.c_str(), out, max);
|
|
||||||
}
|
|
||||||
// Parses the signed decimal or hexadecimal integer in the string 's' and sets
|
|
||||||
// 'out' to that value if it is specified. Optionally allows the caller to define
|
|
||||||
// a 'min' and 'max' beyond which otherwise valid values will be rejected. Returns
|
|
||||||
// boolean success; 'out' is untouched if parsing fails.
|
|
||||||
template <typename T>
|
|
||||||
bool ParseInt(const char* s, T* out,
|
|
||||||
T min = std::numeric_limits<T>::min(),
|
|
||||||
T max = std::numeric_limits<T>::max()) {
|
|
||||||
static_assert(std::is_signed<T>::value, "ParseInt can only be used with signed types");
|
|
||||||
while (isspace(*s)) {
|
|
||||||
s++;
|
|
||||||
}
|
|
||||||
int base = (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) ? 16 : 10;
|
|
||||||
errno = 0;
|
|
||||||
char* end;
|
|
||||||
long long int result = strtoll(s, &end, base);
|
|
||||||
if (errno != 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (s == end || *end != '\0') {
|
|
||||||
errno = EINVAL;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (result < min || max < result) {
|
|
||||||
errno = ERANGE;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (out != nullptr) {
|
|
||||||
*out = static_cast<T>(result);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// TODO: string_view
|
|
||||||
template <typename T>
|
|
||||||
bool ParseInt(const std::string& s, T* out,
|
|
||||||
T min = std::numeric_limits<T>::min(),
|
|
||||||
T max = std::numeric_limits<T>::max()) {
|
|
||||||
return ParseInt(s.c_str(), out, min, max);
|
|
||||||
}
|
|
||||||
} // namespace base
|
|
||||||
} // namespace android
|
|
||||||
|
|
@ -1,96 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2015 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
#include "android-base/strings.h"
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
namespace android {
|
|
||||||
namespace base {
|
|
||||||
#define CHECK_NE(a, b) \
|
|
||||||
if ((a) == (b)) abort();
|
|
||||||
std::vector<std::string> Split(const std::string& s,
|
|
||||||
const std::string& delimiters) {
|
|
||||||
CHECK_NE(delimiters.size(), 0U);
|
|
||||||
std::vector<std::string> result;
|
|
||||||
size_t base = 0;
|
|
||||||
size_t found;
|
|
||||||
while (true) {
|
|
||||||
found = s.find_first_of(delimiters, base);
|
|
||||||
result.push_back(s.substr(base, found - base));
|
|
||||||
if (found == s.npos) break;
|
|
||||||
base = found + 1;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
std::string Trim(const std::string& s) {
|
|
||||||
std::string result;
|
|
||||||
if (s.size() == 0) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
size_t start_index = 0;
|
|
||||||
size_t end_index = s.size() - 1;
|
|
||||||
// Skip initial whitespace.
|
|
||||||
while (start_index < s.size()) {
|
|
||||||
if (!isspace(s[start_index])) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
start_index++;
|
|
||||||
}
|
|
||||||
// Skip terminating whitespace.
|
|
||||||
while (end_index >= start_index) {
|
|
||||||
if (!isspace(s[end_index])) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
end_index--;
|
|
||||||
}
|
|
||||||
// All spaces, no beef.
|
|
||||||
if (end_index < start_index) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
// Start_index is the first non-space, end_index is the last one.
|
|
||||||
return s.substr(start_index, end_index - start_index + 1);
|
|
||||||
}
|
|
||||||
// These cases are probably the norm, so we mark them extern in the header to
|
|
||||||
// aid compile time and binary size.
|
|
||||||
template std::string Join(const std::vector<std::string>&, char);
|
|
||||||
template std::string Join(const std::vector<const char*>&, char);
|
|
||||||
template std::string Join(const std::vector<std::string>&, const std::string&);
|
|
||||||
template std::string Join(const std::vector<const char*>&, const std::string&);
|
|
||||||
bool StartsWith(std::string_view s, std::string_view prefix) {
|
|
||||||
return s.substr(0, prefix.size()) == prefix;
|
|
||||||
}
|
|
||||||
bool StartsWith(std::string_view s, char prefix) {
|
|
||||||
return !s.empty() && s.front() == prefix;
|
|
||||||
}
|
|
||||||
bool StartsWithIgnoreCase(std::string_view s, std::string_view prefix) {
|
|
||||||
return s.size() >= prefix.size() && strncasecmp(s.data(), prefix.data(), prefix.size()) == 0;
|
|
||||||
}
|
|
||||||
bool EndsWith(std::string_view s, std::string_view suffix) {
|
|
||||||
return s.size() >= suffix.size() && s.substr(s.size() - suffix.size(), suffix.size()) == suffix;
|
|
||||||
}
|
|
||||||
bool EndsWith(std::string_view s, char suffix) {
|
|
||||||
return !s.empty() && s.back() == suffix;
|
|
||||||
}
|
|
||||||
bool EndsWithIgnoreCase(std::string_view s, std::string_view suffix) {
|
|
||||||
return s.size() >= suffix.size() &&
|
|
||||||
strncasecmp(s.data() + (s.size() - suffix.size()), suffix.data(), suffix.size()) == 0;
|
|
||||||
}
|
|
||||||
bool EqualsIgnoreCase(std::string_view lhs, std::string_view rhs) {
|
|
||||||
return lhs.size() == rhs.size() && strncasecmp(lhs.data(), rhs.data(), lhs.size()) == 0;
|
|
||||||
}
|
|
||||||
} // namespace base
|
|
||||||
} // namespace android
|
|
||||||
|
|
@ -1,75 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2015 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
#include <sstream>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
#include <vector>
|
|
||||||
namespace android {
|
|
||||||
namespace base {
|
|
||||||
// Splits a string into a vector of strings.
|
|
||||||
//
|
|
||||||
// The string is split at each occurrence of a character in delimiters.
|
|
||||||
//
|
|
||||||
// The empty string is not a valid delimiter list.
|
|
||||||
std::vector<std::string> Split(const std::string& s,
|
|
||||||
const std::string& delimiters);
|
|
||||||
// Trims whitespace off both ends of the given string.
|
|
||||||
std::string Trim(const std::string& s);
|
|
||||||
// Joins a container of things into a single string, using the given separator.
|
|
||||||
template <typename ContainerT, typename SeparatorT>
|
|
||||||
std::string Join(const ContainerT& things, SeparatorT separator) {
|
|
||||||
if (things.empty()) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
std::ostringstream result;
|
|
||||||
result << *things.begin();
|
|
||||||
for (auto it = std::next(things.begin()); it != things.end(); ++it) {
|
|
||||||
result << separator << *it;
|
|
||||||
}
|
|
||||||
return result.str();
|
|
||||||
}
|
|
||||||
// We instantiate the common cases in strings.cpp.
|
|
||||||
extern template std::string Join(const std::vector<std::string>&, char);
|
|
||||||
extern template std::string Join(const std::vector<const char*>&, char);
|
|
||||||
extern template std::string Join(const std::vector<std::string>&, const std::string&);
|
|
||||||
extern template std::string Join(const std::vector<const char*>&, const std::string&);
|
|
||||||
// Tests whether 's' starts with 'prefix'.
|
|
||||||
bool StartsWith(std::string_view s, std::string_view prefix);
|
|
||||||
bool StartsWith(std::string_view s, char prefix);
|
|
||||||
bool StartsWithIgnoreCase(std::string_view s, std::string_view prefix);
|
|
||||||
// Tests whether 's' ends with 'suffix'.
|
|
||||||
bool EndsWith(std::string_view s, std::string_view suffix);
|
|
||||||
bool EndsWith(std::string_view s, char suffix);
|
|
||||||
bool EndsWithIgnoreCase(std::string_view s, std::string_view suffix);
|
|
||||||
// Tests whether 'lhs' equals 'rhs', ignoring case.
|
|
||||||
bool EqualsIgnoreCase(std::string_view lhs, std::string_view rhs);
|
|
||||||
// Removes `prefix` from the start of the given string and returns true (if
|
|
||||||
// it was present), false otherwise.
|
|
||||||
inline bool ConsumePrefix(std::string_view* s, std::string_view prefix) {
|
|
||||||
if (!StartsWith(*s, prefix)) return false;
|
|
||||||
s->remove_prefix(prefix.size());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// Removes `suffix` from the end of the given string and returns true (if
|
|
||||||
// it was present), false otherwise.
|
|
||||||
inline bool ConsumeSuffix(std::string_view* s, std::string_view suffix) {
|
|
||||||
if (!EndsWith(*s, suffix)) return false;
|
|
||||||
s->remove_suffix(suffix.size());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} // namespace base
|
|
||||||
} // namespace android
|
|
||||||
|
|
@ -1,109 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2016 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
#include <mutex>
|
|
||||||
#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
|
|
||||||
#define CAPABILITY(x) \
|
|
||||||
THREAD_ANNOTATION_ATTRIBUTE__(capability(x))
|
|
||||||
#define SCOPED_CAPABILITY \
|
|
||||||
THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
|
|
||||||
#define SHARED_CAPABILITY(...) \
|
|
||||||
THREAD_ANNOTATION_ATTRIBUTE__(shared_capability(__VA_ARGS__))
|
|
||||||
#define GUARDED_BY(x) \
|
|
||||||
THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
|
|
||||||
#define PT_GUARDED_BY(x) \
|
|
||||||
THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))
|
|
||||||
#define EXCLUSIVE_LOCKS_REQUIRED(...) \
|
|
||||||
THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__))
|
|
||||||
#define SHARED_LOCKS_REQUIRED(...) \
|
|
||||||
THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__))
|
|
||||||
#define ACQUIRED_BEFORE(...) \
|
|
||||||
THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))
|
|
||||||
#define ACQUIRED_AFTER(...) \
|
|
||||||
THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__))
|
|
||||||
#define REQUIRES(...) \
|
|
||||||
THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__))
|
|
||||||
#define REQUIRES_SHARED(...) \
|
|
||||||
THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(__VA_ARGS__))
|
|
||||||
#define ACQUIRE(...) \
|
|
||||||
THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__))
|
|
||||||
#define ACQUIRE_SHARED(...) \
|
|
||||||
THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__))
|
|
||||||
#define RELEASE(...) \
|
|
||||||
THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__))
|
|
||||||
#define RELEASE_SHARED(...) \
|
|
||||||
THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__))
|
|
||||||
#define TRY_ACQUIRE(...) \
|
|
||||||
THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__))
|
|
||||||
#define TRY_ACQUIRE_SHARED(...) \
|
|
||||||
THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__))
|
|
||||||
#define EXCLUDES(...) \
|
|
||||||
THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))
|
|
||||||
#define ASSERT_CAPABILITY(x) \
|
|
||||||
THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x))
|
|
||||||
#define ASSERT_SHARED_CAPABILITY(x) \
|
|
||||||
THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x))
|
|
||||||
#define RETURN_CAPABILITY(x) \
|
|
||||||
THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
|
|
||||||
#define EXCLUSIVE_LOCK_FUNCTION(...) \
|
|
||||||
THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__))
|
|
||||||
#define EXCLUSIVE_TRYLOCK_FUNCTION(...) \
|
|
||||||
THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__))
|
|
||||||
#define SHARED_LOCK_FUNCTION(...) \
|
|
||||||
THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__))
|
|
||||||
#define SHARED_TRYLOCK_FUNCTION(...) \
|
|
||||||
THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__))
|
|
||||||
#define UNLOCK_FUNCTION(...) \
|
|
||||||
THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__))
|
|
||||||
#define SCOPED_LOCKABLE \
|
|
||||||
THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
|
|
||||||
#define LOCK_RETURNED(x) \
|
|
||||||
THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
|
|
||||||
#define NO_THREAD_SAFETY_ANALYSIS \
|
|
||||||
THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
|
|
||||||
namespace android {
|
|
||||||
namespace base {
|
|
||||||
// A class to help thread safety analysis deal with std::unique_lock and condition_variable.
|
|
||||||
//
|
|
||||||
// Clang's thread safety analysis currently doesn't perform alias analysis, so movable types
|
|
||||||
// like std::unique_lock can't be marked with thread safety annotations. This helper allows
|
|
||||||
// for manual assertion of lock state in a scope.
|
|
||||||
//
|
|
||||||
// For example:
|
|
||||||
//
|
|
||||||
// std::mutex mutex;
|
|
||||||
// std::condition_variable cv;
|
|
||||||
// std::vector<int> vec GUARDED_BY(mutex);
|
|
||||||
//
|
|
||||||
// int pop() {
|
|
||||||
// std::unique_lock lock(mutex);
|
|
||||||
// ScopedLockAssertion lock_assertion(mutex);
|
|
||||||
// cv.wait(lock, []() {
|
|
||||||
// ScopedLockAssertion lock_assertion(mutex);
|
|
||||||
// return !vec.empty();
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// int result = vec.back();
|
|
||||||
// vec.pop_back();
|
|
||||||
// return result;
|
|
||||||
// }
|
|
||||||
class SCOPED_CAPABILITY ScopedLockAssertion {
|
|
||||||
public:
|
|
||||||
ScopedLockAssertion(std::mutex& mutex) ACQUIRE(mutex) {}
|
|
||||||
~ScopedLockAssertion() RELEASE() {}
|
|
||||||
};
|
|
||||||
} // namespace base
|
|
||||||
} // namespace android
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2018 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
#include <android-base/threads.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#if defined(__APPLE__)
|
|
||||||
#include <pthread.h>
|
|
||||||
#elif defined(__linux__) && !defined(__ANDROID__)
|
|
||||||
#include <syscall.h>
|
|
||||||
#elif defined(_WIN32)
|
|
||||||
#include <windows.h>
|
|
||||||
#endif
|
|
||||||
namespace android {
|
|
||||||
namespace base {
|
|
||||||
uint64_t GetThreadId() {
|
|
||||||
#if defined(__BIONIC__)
|
|
||||||
return gettid();
|
|
||||||
#elif defined(__APPLE__)
|
|
||||||
uint64_t tid;
|
|
||||||
pthread_threadid_np(NULL, &tid);
|
|
||||||
return tid;
|
|
||||||
#elif defined(__linux__)
|
|
||||||
return syscall(__NR_gettid);
|
|
||||||
#elif defined(_WIN32)
|
|
||||||
return GetCurrentThreadId();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
} // namespace base
|
|
||||||
} // namespace android
|
|
||||||
#if defined(__GLIBC__)
|
|
||||||
int tgkill(int tgid, int tid, int sig) {
|
|
||||||
return syscall(__NR_tgkill, tgid, tid, sig);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2018 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
#include <stdint.h>
|
|
||||||
namespace android {
|
|
||||||
namespace base {
|
|
||||||
uint64_t GetThreadId();
|
|
||||||
}
|
|
||||||
} // namespace android
|
|
||||||
#if defined(__GLIBC__)
|
|
||||||
// bionic has this Linux-specifix call, but glibc doesn't.
|
|
||||||
extern "C" int tgkill(int tgid, int tid, int sig);
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,244 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2015 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
#include <dirent.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#if !defined(_WIN32)
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#endif
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
// DO NOT INCLUDE OTHER LIBBASE HEADERS!
|
|
||||||
// This file gets used in libbinder, and libbinder is used everywhere.
|
|
||||||
// Including other headers from libbase frequently results in inclusion of
|
|
||||||
// android-base/macros.h, which causes macro collisions.
|
|
||||||
// Container for a file descriptor that automatically closes the descriptor as
|
|
||||||
// it goes out of scope.
|
|
||||||
//
|
|
||||||
// unique_fd ufd(open("/some/path", "r"));
|
|
||||||
// if (ufd.get() == -1) return error;
|
|
||||||
//
|
|
||||||
// // Do something useful, possibly including 'return'.
|
|
||||||
//
|
|
||||||
// return 0; // Descriptor is closed for you.
|
|
||||||
//
|
|
||||||
// unique_fd is also known as ScopedFd/ScopedFD/scoped_fd; mentioned here to help
|
|
||||||
// you find this class if you're searching for one of those names.
|
|
||||||
#if defined(__BIONIC__)
|
|
||||||
#include <android/fdsan.h>
|
|
||||||
#endif
|
|
||||||
namespace android {
|
|
||||||
namespace base {
|
|
||||||
struct DefaultCloser {
|
|
||||||
#if defined(__BIONIC__)
|
|
||||||
static void Tag(int fd, void* old_addr, void* new_addr) {
|
|
||||||
if (android_fdsan_exchange_owner_tag) {
|
|
||||||
uint64_t old_tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_UNIQUE_FD,
|
|
||||||
reinterpret_cast<uint64_t>(old_addr));
|
|
||||||
uint64_t new_tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_UNIQUE_FD,
|
|
||||||
reinterpret_cast<uint64_t>(new_addr));
|
|
||||||
android_fdsan_exchange_owner_tag(fd, old_tag, new_tag);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static void Close(int fd, void* addr) {
|
|
||||||
if (android_fdsan_close_with_tag) {
|
|
||||||
uint64_t tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_UNIQUE_FD,
|
|
||||||
reinterpret_cast<uint64_t>(addr));
|
|
||||||
android_fdsan_close_with_tag(fd, tag);
|
|
||||||
} else {
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
static void Close(int fd) {
|
|
||||||
// Even if close(2) fails with EINTR, the fd will have been closed.
|
|
||||||
// Using TEMP_FAILURE_RETRY will either lead to EBADF or closing someone
|
|
||||||
// else's fd.
|
|
||||||
// http://lkml.indiana.edu/hypermail/linux/kernel/0509.1/0877.html
|
|
||||||
::close(fd);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
template <typename Closer>
|
|
||||||
class unique_fd_impl final {
|
|
||||||
public:
|
|
||||||
unique_fd_impl() {}
|
|
||||||
explicit unique_fd_impl(int fd) { reset(fd); }
|
|
||||||
~unique_fd_impl() { reset(); }
|
|
||||||
unique_fd_impl(unique_fd_impl&& other) noexcept { reset(other.release()); }
|
|
||||||
unique_fd_impl& operator=(unique_fd_impl&& s) noexcept {
|
|
||||||
int fd = s.fd_;
|
|
||||||
s.fd_ = -1;
|
|
||||||
reset(fd, &s);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
void reset(int new_value = -1) { reset(new_value, nullptr); }
|
|
||||||
int get() const { return fd_; }
|
|
||||||
#if !defined(ANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION)
|
|
||||||
// unique_fd's operator int is dangerous, but we have way too much code that
|
|
||||||
// depends on it, so make this opt-in at first.
|
|
||||||
operator int() const { return get(); } // NOLINT
|
|
||||||
#endif
|
|
||||||
bool operator>=(int rhs) const { return get() >= rhs; }
|
|
||||||
bool operator<(int rhs) const { return get() < rhs; }
|
|
||||||
bool operator==(int rhs) const { return get() == rhs; }
|
|
||||||
bool operator!=(int rhs) const { return get() != rhs; }
|
|
||||||
// Catch bogus error checks (i.e.: "!fd" instead of "fd != -1").
|
|
||||||
bool operator!() const = delete;
|
|
||||||
int release() __attribute__((warn_unused_result)) {
|
|
||||||
tag(fd_, this, nullptr);
|
|
||||||
int ret = fd_;
|
|
||||||
fd_ = -1;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
void reset(int new_value, void* previous_tag) {
|
|
||||||
int previous_errno = errno;
|
|
||||||
if (fd_ != -1) {
|
|
||||||
close(fd_, this);
|
|
||||||
}
|
|
||||||
fd_ = new_value;
|
|
||||||
if (new_value != -1) {
|
|
||||||
tag(new_value, previous_tag, this);
|
|
||||||
}
|
|
||||||
errno = previous_errno;
|
|
||||||
}
|
|
||||||
int fd_ = -1;
|
|
||||||
// Template magic to use Closer::Tag if available, and do nothing if not.
|
|
||||||
// If Closer::Tag exists, this implementation is preferred, because int is a better match.
|
|
||||||
// If not, this implementation is SFINAEd away, and the no-op below is the only one that exists.
|
|
||||||
template <typename T = Closer>
|
|
||||||
static auto tag(int fd, void* old_tag, void* new_tag)
|
|
||||||
-> decltype(T::Tag(fd, old_tag, new_tag), void()) {
|
|
||||||
T::Tag(fd, old_tag, new_tag);
|
|
||||||
}
|
|
||||||
template <typename T = Closer>
|
|
||||||
static void tag(long, void*, void*) {
|
|
||||||
// No-op.
|
|
||||||
}
|
|
||||||
// Same as above, to select between Closer::Close(int) and Closer::Close(int, void*).
|
|
||||||
template <typename T = Closer>
|
|
||||||
static auto close(int fd, void* tag_value) -> decltype(T::Close(fd, tag_value), void()) {
|
|
||||||
T::Close(fd, tag_value);
|
|
||||||
}
|
|
||||||
template <typename T = Closer>
|
|
||||||
static auto close(int fd, void*) -> decltype(T::Close(fd), void()) {
|
|
||||||
T::Close(fd);
|
|
||||||
}
|
|
||||||
unique_fd_impl(const unique_fd_impl&);
|
|
||||||
void operator=(const unique_fd_impl&);
|
|
||||||
};
|
|
||||||
using unique_fd = unique_fd_impl<DefaultCloser>;
|
|
||||||
#if !defined(_WIN32)
|
|
||||||
// Inline functions, so that they can be used header-only.
|
|
||||||
template <typename Closer>
|
|
||||||
inline bool Pipe(unique_fd_impl<Closer>* read, unique_fd_impl<Closer>* write,
|
|
||||||
int flags = O_CLOEXEC) {
|
|
||||||
int pipefd[2];
|
|
||||||
#if defined(__linux__)
|
|
||||||
if (pipe2(pipefd, flags) != 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#else // defined(__APPLE__)
|
|
||||||
if (flags & ~(O_CLOEXEC | O_NONBLOCK)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (pipe(pipefd) != 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (flags & O_CLOEXEC) {
|
|
||||||
if (fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) != 0 || fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) != 0) {
|
|
||||||
close(pipefd[0]);
|
|
||||||
close(pipefd[1]);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (flags & O_NONBLOCK) {
|
|
||||||
if (fcntl(pipefd[0], F_SETFL, O_NONBLOCK) != 0 || fcntl(pipefd[1], F_SETFL, O_NONBLOCK) != 0) {
|
|
||||||
close(pipefd[0]);
|
|
||||||
close(pipefd[1]);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
read->reset(pipefd[0]);
|
|
||||||
write->reset(pipefd[1]);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
template <typename Closer>
|
|
||||||
inline bool Socketpair(int domain, int type, int protocol, unique_fd_impl<Closer>* left,
|
|
||||||
unique_fd_impl<Closer>* right) {
|
|
||||||
int sockfd[2];
|
|
||||||
if (socketpair(domain, type, protocol, sockfd) != 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
left->reset(sockfd[0]);
|
|
||||||
right->reset(sockfd[1]);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
template <typename Closer>
|
|
||||||
inline bool Socketpair(int type, unique_fd_impl<Closer>* left, unique_fd_impl<Closer>* right) {
|
|
||||||
return Socketpair(AF_UNIX, type, 0, left, right);
|
|
||||||
}
|
|
||||||
// Using fdopen with unique_fd correctly is more annoying than it should be,
|
|
||||||
// because fdopen doesn't close the file descriptor received upon failure.
|
|
||||||
inline FILE* Fdopen(unique_fd&& ufd, const char* mode) {
|
|
||||||
int fd = ufd.release();
|
|
||||||
FILE* file = fdopen(fd, mode);
|
|
||||||
if (!file) {
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
// Using fdopendir with unique_fd correctly is more annoying than it should be,
|
|
||||||
// because fdopen doesn't close the file descriptor received upon failure.
|
|
||||||
inline DIR* Fdopendir(unique_fd&& ufd) {
|
|
||||||
int fd = ufd.release();
|
|
||||||
DIR* dir = fdopendir(fd);
|
|
||||||
if (dir == nullptr) {
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
return dir;
|
|
||||||
}
|
|
||||||
#endif // !defined(_WIN32)
|
|
||||||
// A wrapper type that can be implicitly constructed from either int or unique_fd.
|
|
||||||
struct borrowed_fd {
|
|
||||||
/* implicit */ borrowed_fd(int fd) : fd_(fd) {}
|
|
||||||
template <typename T>
|
|
||||||
/* implicit */ borrowed_fd(const unique_fd_impl<T>& ufd) : fd_(ufd.get()) {}
|
|
||||||
int get() const { return fd_; }
|
|
||||||
bool operator>=(int rhs) const { return get() >= rhs; }
|
|
||||||
bool operator<(int rhs) const { return get() < rhs; }
|
|
||||||
bool operator==(int rhs) const { return get() == rhs; }
|
|
||||||
bool operator!=(int rhs) const { return get() != rhs; }
|
|
||||||
private:
|
|
||||||
int fd_ = -1;
|
|
||||||
};
|
|
||||||
} // namespace base
|
|
||||||
} // namespace android
|
|
||||||
template <typename T>
|
|
||||||
int close(const android::base::unique_fd_impl<T>&)
|
|
||||||
__attribute__((__unavailable__("close called on unique_fd")));
|
|
||||||
template <typename T>
|
|
||||||
FILE* fdopen(const android::base::unique_fd_impl<T>&, const char* mode)
|
|
||||||
__attribute__((__unavailable__("fdopen takes ownership of the fd passed in; either dup the "
|
|
||||||
"unique_fd, or use android::base::Fdopen to pass ownership")));
|
|
||||||
template <typename T>
|
|
||||||
DIR* fdopendir(const android::base::unique_fd_impl<T>&) __attribute__((
|
|
||||||
__unavailable__("fdopendir takes ownership of the fd passed in; either dup the "
|
|
||||||
"unique_fd, or use android::base::Fdopendir to pass ownership")));
|
|
||||||
|
|
@ -1,91 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2015 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <string>
|
|
||||||
#else
|
|
||||||
// Bring in prototypes for standard APIs so that we can import them into the utf8 namespace.
|
|
||||||
#include <fcntl.h> // open
|
|
||||||
#include <stdio.h> // fopen
|
|
||||||
#include <sys/stat.h> // mkdir
|
|
||||||
#include <unistd.h> // unlink
|
|
||||||
#endif
|
|
||||||
namespace android {
|
|
||||||
namespace base {
|
|
||||||
// Only available on Windows because this is only needed on Windows.
|
|
||||||
#ifdef _WIN32
|
|
||||||
// Convert size number of UTF-16 wchar_t's to UTF-8. Returns whether the
|
|
||||||
// conversion was done successfully.
|
|
||||||
bool WideToUTF8(const wchar_t* utf16, const size_t size, std::string* utf8);
|
|
||||||
// Convert a NULL-terminated string of UTF-16 characters to UTF-8. Returns
|
|
||||||
// whether the conversion was done successfully.
|
|
||||||
bool WideToUTF8(const wchar_t* utf16, std::string* utf8);
|
|
||||||
// Convert a UTF-16 std::wstring (including any embedded NULL characters) to
|
|
||||||
// UTF-8. Returns whether the conversion was done successfully.
|
|
||||||
bool WideToUTF8(const std::wstring& utf16, std::string* utf8);
|
|
||||||
// Convert size number of UTF-8 char's to UTF-16. Returns whether the conversion
|
|
||||||
// was done successfully.
|
|
||||||
bool UTF8ToWide(const char* utf8, const size_t size, std::wstring* utf16);
|
|
||||||
// Convert a NULL-terminated string of UTF-8 characters to UTF-16. Returns
|
|
||||||
// whether the conversion was done successfully.
|
|
||||||
bool UTF8ToWide(const char* utf8, std::wstring* utf16);
|
|
||||||
// Convert a UTF-8 std::string (including any embedded NULL characters) to
|
|
||||||
// UTF-16. Returns whether the conversion was done successfully.
|
|
||||||
bool UTF8ToWide(const std::string& utf8, std::wstring* utf16);
|
|
||||||
// Convert a file system path, represented as a NULL-terminated string of
|
|
||||||
// UTF-8 characters, to a UTF-16 string representing the same file system
|
|
||||||
// path using the Windows extended-lengh path representation.
|
|
||||||
//
|
|
||||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#MAXPATH:
|
|
||||||
// ```The Windows API has many functions that also have Unicode versions to
|
|
||||||
// permit an extended-length path for a maximum total path length of 32,767
|
|
||||||
// characters. To specify an extended-length path, use the "\\?\" prefix.
|
|
||||||
// For example, "\\?\D:\very long path".```
|
|
||||||
//
|
|
||||||
// Returns whether the conversion was done successfully.
|
|
||||||
bool UTF8PathToWindowsLongPath(const char* utf8, std::wstring* utf16);
|
|
||||||
#endif
|
|
||||||
// The functions in the utf8 namespace take UTF-8 strings. For Windows, these
|
|
||||||
// are wrappers, for non-Windows these just expose existing APIs. To call these
|
|
||||||
// functions, use:
|
|
||||||
//
|
|
||||||
// // anonymous namespace to avoid conflict with existing open(), unlink(), etc.
|
|
||||||
// namespace {
|
|
||||||
// // Import functions into anonymous namespace.
|
|
||||||
// using namespace android::base::utf8;
|
|
||||||
//
|
|
||||||
// void SomeFunction(const char* name) {
|
|
||||||
// int fd = open(name, ...); // Calls android::base::utf8::open().
|
|
||||||
// ...
|
|
||||||
// unlink(name); // Calls android::base::utf8::unlink().
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
namespace utf8 {
|
|
||||||
#ifdef _WIN32
|
|
||||||
FILE* fopen(const char* name, const char* mode);
|
|
||||||
int mkdir(const char* name, mode_t mode);
|
|
||||||
int open(const char* name, int flags, ...);
|
|
||||||
int unlink(const char* name);
|
|
||||||
#else
|
|
||||||
using ::fopen;
|
|
||||||
using ::mkdir;
|
|
||||||
using ::open;
|
|
||||||
using ::unlink;
|
|
||||||
#endif
|
|
||||||
} // namespace utf8
|
|
||||||
} // namespace base
|
|
||||||
} // namespace android
|
|
||||||
|
|
@ -1,170 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2018 The Android Open Source Project
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in
|
|
||||||
* the documentation and/or other materials provided with the
|
|
||||||
* distribution.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
||||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
||||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
||||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
||||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
|
||||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
|
||||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
||||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
|
||||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
* SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <sys/cdefs.h>
|
|
||||||
__BEGIN_DECLS
|
|
||||||
/*
|
|
||||||
* Error checking for close(2).
|
|
||||||
*
|
|
||||||
* Mishandling of file descriptor ownership is a common source of errors that
|
|
||||||
* can be extremely difficult to diagnose. Mistakes like the following can
|
|
||||||
* result in seemingly 'impossible' failures showing up on other threads that
|
|
||||||
* happened to try to open a file descriptor between the buggy code's close and
|
|
||||||
* fclose:
|
|
||||||
*
|
|
||||||
* int print(int fd) {
|
|
||||||
* int rc;
|
|
||||||
* char buf[128];
|
|
||||||
* while ((rc = read(fd, buf, sizeof(buf))) > 0) {
|
|
||||||
* printf("%.*s", rc);
|
|
||||||
* }
|
|
||||||
* close(fd);
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* int bug() {
|
|
||||||
* FILE* f = fopen("foo", "r");
|
|
||||||
* print(fileno(f));
|
|
||||||
* fclose(f);
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* To make it easier to find this class of bugs, bionic provides a method to
|
|
||||||
* require that file descriptors are closed by their owners. File descriptors
|
|
||||||
* can be associated with tags with which they must be closed. This allows
|
|
||||||
* objects that conceptually own an fd (FILE*, unique_fd, etc.) to use their
|
|
||||||
* own address at the tag, to enforce that closure of the fd must come as a
|
|
||||||
* result of their own destruction (fclose, ~unique_fd, etc.)
|
|
||||||
*
|
|
||||||
* By default, a file descriptor's tag is 0, and close(fd) is equivalent to
|
|
||||||
* closing fd with the tag 0.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
* For improved diagnostics, the type of a file descriptors owner can be
|
|
||||||
* encoded in the most significant byte of the owner tag. Values of 0 and 0xff
|
|
||||||
* are ignored, which allows for raw pointers to be used as owner tags without
|
|
||||||
* modification.
|
|
||||||
*/
|
|
||||||
enum android_fdsan_owner_type {
|
|
||||||
/*
|
|
||||||
* Generic Java or native owners.
|
|
||||||
*
|
|
||||||
* Generic Java objects always use 255 as their type, using identityHashCode
|
|
||||||
* as the value of the tag, leaving bits 33-56 unset. Native pointers are sign
|
|
||||||
* extended from 48-bits of virtual address space, and so can have the MSB
|
|
||||||
* set to 255 as well. Use the value of bits 49-56 to distinguish between
|
|
||||||
* these cases.
|
|
||||||
*/
|
|
||||||
ANDROID_FDSAN_OWNER_TYPE_GENERIC_00 = 0,
|
|
||||||
ANDROID_FDSAN_OWNER_TYPE_GENERIC_FF = 255,
|
|
||||||
/* FILE* */
|
|
||||||
ANDROID_FDSAN_OWNER_TYPE_FILE = 1,
|
|
||||||
/* DIR* */
|
|
||||||
ANDROID_FDSAN_OWNER_TYPE_DIR = 2,
|
|
||||||
/* android::base::unique_fd */
|
|
||||||
ANDROID_FDSAN_OWNER_TYPE_UNIQUE_FD = 3,
|
|
||||||
/* sqlite-owned file descriptors */
|
|
||||||
ANDROID_FDSAN_OWNER_TYPE_SQLITE = 4,
|
|
||||||
/* java.io.FileInputStream */
|
|
||||||
ANDROID_FDSAN_OWNER_TYPE_FILEINPUTSTREAM = 5,
|
|
||||||
/* java.io.FileOutputStream */
|
|
||||||
ANDROID_FDSAN_OWNER_TYPE_FILEOUTPUTSTREAM = 6,
|
|
||||||
/* java.io.RandomAccessFile */
|
|
||||||
ANDROID_FDSAN_OWNER_TYPE_RANDOMACCESSFILE = 7,
|
|
||||||
/* android.os.ParcelFileDescriptor */
|
|
||||||
ANDROID_FDSAN_OWNER_TYPE_PARCELFILEDESCRIPTOR = 8,
|
|
||||||
/* ART FdFile */
|
|
||||||
ANDROID_FDSAN_OWNER_TYPE_ART_FDFILE = 9,
|
|
||||||
/* java.net.DatagramSocketImpl */
|
|
||||||
ANDROID_FDSAN_OWNER_TYPE_DATAGRAMSOCKETIMPL = 10,
|
|
||||||
/* java.net.SocketImpl */
|
|
||||||
ANDROID_FDSAN_OWNER_TYPE_SOCKETIMPL = 11,
|
|
||||||
/* libziparchive's ZipArchive */
|
|
||||||
ANDROID_FDSAN_OWNER_TYPE_ZIPARCHIVE = 12,
|
|
||||||
};
|
|
||||||
/*
|
|
||||||
* Create an owner tag with the specified type and least significant 56 bits of tag.
|
|
||||||
*/
|
|
||||||
uint64_t android_fdsan_create_owner_tag(enum android_fdsan_owner_type type, uint64_t tag) __INTRODUCED_IN(29) __attribute__((__weak__));
|
|
||||||
/*
|
|
||||||
* Exchange a file descriptor's tag.
|
|
||||||
*
|
|
||||||
* Logs and aborts if the fd's tag does not match expected_tag.
|
|
||||||
*/
|
|
||||||
void android_fdsan_exchange_owner_tag(int fd, uint64_t expected_tag, uint64_t new_tag) __INTRODUCED_IN(29) __attribute__((__weak__));
|
|
||||||
/*
|
|
||||||
* Close a file descriptor with a tag, and resets the tag to 0.
|
|
||||||
*
|
|
||||||
* Logs and aborts if the tag is incorrect.
|
|
||||||
*/
|
|
||||||
int android_fdsan_close_with_tag(int fd, uint64_t tag) __INTRODUCED_IN(29) __attribute__((__weak__));
|
|
||||||
/*
|
|
||||||
* Get a file descriptor's current owner tag.
|
|
||||||
*
|
|
||||||
* Returns 0 for untagged and invalid file descriptors.
|
|
||||||
*/
|
|
||||||
uint64_t android_fdsan_get_owner_tag(int fd) __INTRODUCED_IN(29);
|
|
||||||
/*
|
|
||||||
* Get an owner tag's string representation.
|
|
||||||
*
|
|
||||||
* The return value points to memory with static lifetime, do not attempt to modify it.
|
|
||||||
*/
|
|
||||||
const char* android_fdsan_get_tag_type(uint64_t tag) __INTRODUCED_IN(29);
|
|
||||||
/*
|
|
||||||
* Get an owner tag's value, with the type masked off.
|
|
||||||
*/
|
|
||||||
uint64_t android_fdsan_get_tag_value(uint64_t tag) __INTRODUCED_IN(29);
|
|
||||||
enum android_fdsan_error_level {
|
|
||||||
// No errors.
|
|
||||||
ANDROID_FDSAN_ERROR_LEVEL_DISABLED,
|
|
||||||
// Warn once(ish) on error, and then downgrade to ANDROID_FDSAN_ERROR_LEVEL_DISABLED.
|
|
||||||
ANDROID_FDSAN_ERROR_LEVEL_WARN_ONCE,
|
|
||||||
// Warn always on error.
|
|
||||||
ANDROID_FDSAN_ERROR_LEVEL_WARN_ALWAYS,
|
|
||||||
// Abort on error.
|
|
||||||
ANDROID_FDSAN_ERROR_LEVEL_FATAL,
|
|
||||||
};
|
|
||||||
/*
|
|
||||||
* Get the error level.
|
|
||||||
*/
|
|
||||||
enum android_fdsan_error_level android_fdsan_get_error_level() __INTRODUCED_IN(29) __attribute__((__weak__));
|
|
||||||
/*
|
|
||||||
* Set the error level and return the previous state.
|
|
||||||
*
|
|
||||||
* Error checking is automatically disabled in the child of a fork, to maintain
|
|
||||||
* compatibility with code that forks, blindly closes FDs, and then execs.
|
|
||||||
*
|
|
||||||
* In cases such as the zygote, where the child has no intention of calling
|
|
||||||
* exec, call this function to reenable fdsan checks.
|
|
||||||
*
|
|
||||||
* This function is not thread-safe and does not synchronize with checks of the
|
|
||||||
* value, and so should probably only be called in single-threaded contexts
|
|
||||||
* (e.g. postfork).
|
|
||||||
*/
|
|
||||||
enum android_fdsan_error_level android_fdsan_set_error_level(enum android_fdsan_error_level new_level) __INTRODUCED_IN(29) __attribute__((__weak__));
|
|
||||||
__END_DECLS
|
|
||||||
|
|
@ -1,82 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2010 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
#include <stddef.h> // for size_t
|
|
||||||
#include <unistd.h> // for TEMP_FAILURE_RETRY
|
|
||||||
#include "android-base/macros.h"
|
|
||||||
#include "android-base/thread_annotations.h"
|
|
||||||
// Declare a friend relationship in a class with a test. Used rather that FRIEND_TEST to avoid
|
|
||||||
// globally importing gtest/gtest.h into the main ART header files.
|
|
||||||
#define ART_FRIEND_TEST(test_set_name, individual_test)\
|
|
||||||
friend class test_set_name##_##individual_test##_Test
|
|
||||||
// Declare a friend relationship in a class with a typed test.
|
|
||||||
#define ART_FRIEND_TYPED_TEST(test_set_name, individual_test)\
|
|
||||||
template<typename T> ART_FRIEND_TEST(test_set_name, individual_test)
|
|
||||||
// A macro to disallow new and delete operators for a class. It goes in the private: declarations.
|
|
||||||
// NOTE: Providing placement new (and matching delete) for constructing container elements.
|
|
||||||
#define DISALLOW_ALLOCATION() \
|
|
||||||
public: \
|
|
||||||
NO_RETURN ALWAYS_INLINE void operator delete(void*, size_t) { UNREACHABLE(); } \
|
|
||||||
ALWAYS_INLINE void* operator new(size_t, void* ptr) noexcept { return ptr; } \
|
|
||||||
ALWAYS_INLINE void operator delete(void*, void*) noexcept { } \
|
|
||||||
private: \
|
|
||||||
void* operator new(size_t) = delete // NOLINT
|
|
||||||
// offsetof is not defined by the spec on types with non-standard layout,
|
|
||||||
// however it is implemented by compilers in practice.
|
|
||||||
// (note that reinterpret_cast is not valid constexpr)
|
|
||||||
//
|
|
||||||
// Alternative approach would be something like:
|
|
||||||
// #define OFFSETOF_HELPER(t, f) \
|
|
||||||
// (reinterpret_cast<uintptr_t>(&reinterpret_cast<t*>(16)->f) - static_cast<uintptr_t>(16u))
|
|
||||||
// #define OFFSETOF_MEMBER(t, f) \
|
|
||||||
// (__builtin_constant_p(OFFSETOF_HELPER(t,f)) ? OFFSETOF_HELPER(t,f) : OFFSETOF_HELPER(t,f))
|
|
||||||
#define OFFSETOF_MEMBER(t, f) offsetof(t, f)
|
|
||||||
#define OFFSETOF_MEMBERPTR(t, f) \
|
|
||||||
(reinterpret_cast<uintptr_t>(&(reinterpret_cast<t*>(16)->*f)) - static_cast<uintptr_t>(16)) // NOLINT
|
|
||||||
#define ALIGNED(x) __attribute__ ((__aligned__(x)))
|
|
||||||
#define PACKED(x) __attribute__ ((__aligned__(x), __packed__))
|
|
||||||
// Stringify the argument.
|
|
||||||
#define QUOTE(x) #x
|
|
||||||
#define STRINGIFY(x) QUOTE(x)
|
|
||||||
// Append tokens after evaluating.
|
|
||||||
#define APPEND_TOKENS_AFTER_EVAL_2(a, b) a ## b
|
|
||||||
#define APPEND_TOKENS_AFTER_EVAL(a, b) APPEND_TOKENS_AFTER_EVAL_2(a, b)
|
|
||||||
#ifndef NDEBUG
|
|
||||||
#define ALWAYS_INLINE
|
|
||||||
#else
|
|
||||||
#define ALWAYS_INLINE __attribute__ ((always_inline))
|
|
||||||
#endif
|
|
||||||
// clang doesn't like attributes on lambda functions. It would be nice to say:
|
|
||||||
// #define ALWAYS_INLINE_LAMBDA ALWAYS_INLINE
|
|
||||||
#define ALWAYS_INLINE_LAMBDA
|
|
||||||
#define NO_INLINE __attribute__ ((noinline))
|
|
||||||
#if defined (__APPLE__)
|
|
||||||
#define HOT_ATTR
|
|
||||||
#define COLD_ATTR
|
|
||||||
#else
|
|
||||||
#define HOT_ATTR __attribute__ ((hot))
|
|
||||||
#define COLD_ATTR __attribute__ ((cold))
|
|
||||||
#endif
|
|
||||||
#define PURE __attribute__ ((__pure__))
|
|
||||||
// Define that a position within code is unreachable, for example:
|
|
||||||
// int foo () { LOG(FATAL) << "Don't call me"; UNREACHABLE(); }
|
|
||||||
// without the UNREACHABLE a return statement would be necessary.
|
|
||||||
#define UNREACHABLE __builtin_unreachable
|
|
||||||
// Add the C++11 noreturn attribute.
|
|
||||||
#define NO_RETURN [[ noreturn ]] // NOLINT[whitespace/braces] [5]
|
|
||||||
// Annotalysis thread-safety analysis support. Things that are not in base.
|
|
||||||
#define LOCKABLE CAPABILITY("mutex")
|
|
||||||
#define SHARED_LOCKABLE SHARED_CAPABILITY("mutex")
|
|
||||||
|
|
@ -1,52 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2017 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <jni.h>
|
|
||||||
#include "android-base/logging.h"
|
|
||||||
#include "nativehelper/scoped_local_ref.h"
|
|
||||||
#include "art/base/macros.h"
|
|
||||||
|
|
||||||
namespace art {
|
|
||||||
ALWAYS_INLINE inline void RegisterNativeMethodsWithClass(JNIEnv* env,
|
|
||||||
jclass clazz,
|
|
||||||
const JNINativeMethod* methods,
|
|
||||||
jint method_count) {
|
|
||||||
ScopedLocalRef<jclass> c(env, clazz);
|
|
||||||
if (clazz == nullptr) {
|
|
||||||
LOG(ERROR) << "clazz is null";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
jint jni_result = env->RegisterNatives(c.get(), methods, method_count);
|
|
||||||
CHECK_EQ(JNI_OK, jni_result);
|
|
||||||
}
|
|
||||||
ALWAYS_INLINE inline void RegisterNativeMethodsWithName(JNIEnv* env,
|
|
||||||
const char* jni_class_name,
|
|
||||||
const JNINativeMethod* methods,
|
|
||||||
jint method_count) {
|
|
||||||
ScopedLocalRef<jclass> clazz(env, env->FindClass(jni_class_name));
|
|
||||||
if (clazz.get() == nullptr) {
|
|
||||||
LOG(FATAL) << "Couldn't find class: " << jni_class_name;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
RegisterNativeMethodsWithClass(env, clazz.get(), methods, method_count);
|
|
||||||
}
|
|
||||||
#define REGISTER_NATIVE_METHODS(jni_class_name) \
|
|
||||||
RegisterNativeMethodsWithName(env, (jni_class_name), gMethods, arraysize(gMethods))
|
|
||||||
|
|
||||||
#define REGISTER_NATIVE_METHODS_WITH_CLASS(clazz) \
|
|
||||||
RegisterNativeMethodsWithClass(env, (clazz), gMethods, arraysize(gMethods))
|
|
||||||
} // namespace art
|
|
||||||
|
|
@ -1,84 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2017 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
* JNI helper macros.
|
|
||||||
*
|
|
||||||
* Only intended to be used in the platform.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
// Intended to construct a JNINativeMethod.
|
|
||||||
// (Assumes the C name is the ClassName_JavaMethodName).
|
|
||||||
#ifndef NATIVE_METHOD
|
|
||||||
#define NATIVE_METHOD(className, functionName, signature) \
|
|
||||||
{ #functionName, \
|
|
||||||
signature, \
|
|
||||||
_NATIVEHELPER_JNI_MACRO_CAST(void*) (className ## _ ## functionName) \
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef LSP_NATIVE_METHOD
|
|
||||||
#define LSP_NATIVE_METHOD(className, functionName, signature) \
|
|
||||||
{ #functionName, \
|
|
||||||
signature, \
|
|
||||||
_NATIVEHELPER_JNI_MACRO_CAST(void*) (Java_io_github_lsposed_lspd_nativebridge_## className ## _ ## functionName) \
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef LSP_DEF_NATIVE_METHOD
|
|
||||||
#define LSP_DEF_NATIVE_METHOD(ret, className, functionName, ...) \
|
|
||||||
extern "C" ret Java_io_github_lsposed_lspd_nativebridge_## className ## _ ## functionName (JNI_START, ## __VA_ARGS__)
|
|
||||||
#endif
|
|
||||||
// Intended to construct a JNINativeMethod (when the C name doesn't match the Java name).
|
|
||||||
// (Assumes the C name is the ClassName_Identifier).
|
|
||||||
#ifndef OVERLOADED_NATIVE_METHOD
|
|
||||||
#define OVERLOADED_NATIVE_METHOD(className, functionName, signature, identifier) \
|
|
||||||
{ #functionName, \
|
|
||||||
signature, \
|
|
||||||
_NATIVEHELPER_JNI_MACRO_CAST(void*) (className ## _ ## identifier) \
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
// Used for methods that are annotated with @FastNative on the managed side.
|
|
||||||
// See NATIVE_METHOD for usage.
|
|
||||||
#ifndef FAST_NATIVE_METHOD
|
|
||||||
#define FAST_NATIVE_METHOD(className, functionName, signature) \
|
|
||||||
{ #functionName, \
|
|
||||||
signature, \
|
|
||||||
_NATIVEHELPER_JNI_MACRO_CAST(void*) (className ## _ ## functionName) \
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
// Used for methods that are annotated with @FastNative on the managed side,
|
|
||||||
// and when the C-name doesn't match the Java-name.
|
|
||||||
//
|
|
||||||
// See OVERLOADED_NATIVE_METHOD for usage.
|
|
||||||
#ifndef OVERLOADED_FAST_NATIVE_METHOD
|
|
||||||
#define OVERLOADED_FAST_NATIVE_METHOD(className, functionName, signature, identifier) \
|
|
||||||
{ #functionName, \
|
|
||||||
signature, \
|
|
||||||
_NATIVEHELPER_JNI_MACRO_CAST(void*) (className ## _ ## identifier) \
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
////////////////////////////////////////////////////////
|
|
||||||
// IMPLEMENTATION ONLY.
|
|
||||||
// DO NOT USE DIRECTLY.
|
|
||||||
////////////////////////////////////////////////////////
|
|
||||||
// C-style cast for C, C++-style cast for C++ to avoid warnings/errors.
|
|
||||||
#if defined(__cplusplus)
|
|
||||||
#define _NATIVEHELPER_JNI_MACRO_CAST(to) \
|
|
||||||
reinterpret_cast<to>
|
|
||||||
#else
|
|
||||||
#define _NATIVEHELPER_JNI_MACRO_CAST(to) \
|
|
||||||
(to)
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2019 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
#ifdef __cplusplus
|
|
||||||
#define MODULE_API extern "C"
|
|
||||||
#else
|
|
||||||
#define MODULE_API
|
|
||||||
#endif // __cplusplus
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2007 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
#pragma
|
|
||||||
#if defined(__cplusplus)
|
|
||||||
#if !defined(DISALLOW_COPY_AND_ASSIGN)
|
|
||||||
// DISALLOW_COPY_AND_ASSIGN disallows the copy and operator= functions. It goes in the private:
|
|
||||||
// declarations in a class.
|
|
||||||
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
|
|
||||||
TypeName(const TypeName&) = delete; \
|
|
||||||
void operator=(const TypeName&) = delete
|
|
||||||
#endif // !defined(DISALLOW_COPY_AND_ASSIGN)
|
|
||||||
#ifndef NATIVEHELPER_JNIHELP_H_
|
|
||||||
// This seems a header-only include. Provide NPE throwing.
|
|
||||||
static inline int jniThrowNullPointerException(JNIEnv* env, const char* msg) {
|
|
||||||
if (env->ExceptionCheck()) {
|
|
||||||
// Drop any pending exception.
|
|
||||||
env->ExceptionClear();
|
|
||||||
}
|
|
||||||
jclass e_class = env->FindClass("java/lang/NullPointerException");
|
|
||||||
if (e_class == nullptr) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (env->ThrowNew(e_class, msg) != JNI_OK) {
|
|
||||||
env->DeleteLocalRef(e_class);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
env->DeleteLocalRef(e_class);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif // NATIVEHELPER_JNIHELP_H_
|
|
||||||
#endif // defined(__cplusplus)
|
|
||||||
|
|
@ -1,71 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2010 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
#include <cstddef>
|
|
||||||
#include "jni.h"
|
|
||||||
#include "nativehelper_utils.h"
|
|
||||||
// A smart pointer that deletes a JNI local reference when it goes out of scope.
|
|
||||||
template<typename T>
|
|
||||||
class ScopedLocalRef {
|
|
||||||
public:
|
|
||||||
ScopedLocalRef(JNIEnv* env, T localRef) : mEnv(env), mLocalRef(localRef) {
|
|
||||||
}
|
|
||||||
ScopedLocalRef(ScopedLocalRef&& s) noexcept : mEnv(s.mEnv), mLocalRef(s.release()) {
|
|
||||||
}
|
|
||||||
explicit ScopedLocalRef(JNIEnv* env) : mEnv(env), mLocalRef(nullptr) {
|
|
||||||
}
|
|
||||||
~ScopedLocalRef() {
|
|
||||||
reset();
|
|
||||||
}
|
|
||||||
void reset(T ptr = NULL) {
|
|
||||||
if (ptr != mLocalRef) {
|
|
||||||
if (mLocalRef != NULL) {
|
|
||||||
mEnv->DeleteLocalRef(mLocalRef);
|
|
||||||
}
|
|
||||||
mLocalRef = ptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
T release() __attribute__((warn_unused_result)) {
|
|
||||||
T localRef = mLocalRef;
|
|
||||||
mLocalRef = NULL;
|
|
||||||
return localRef;
|
|
||||||
}
|
|
||||||
T get() const {
|
|
||||||
return mLocalRef;
|
|
||||||
}
|
|
||||||
// We do not expose an empty constructor as it can easily lead to errors
|
|
||||||
// using common idioms, e.g.:
|
|
||||||
// ScopedLocalRef<...> ref;
|
|
||||||
// ref.reset(...);
|
|
||||||
// Move assignment operator.
|
|
||||||
ScopedLocalRef& operator=(ScopedLocalRef&& s) noexcept {
|
|
||||||
reset(s.release());
|
|
||||||
mEnv = s.mEnv;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
// Allows "if (scoped_ref == nullptr)"
|
|
||||||
bool operator==(std::nullptr_t) const {
|
|
||||||
return mLocalRef == nullptr;
|
|
||||||
}
|
|
||||||
// Allows "if (scoped_ref != nullptr)"
|
|
||||||
bool operator!=(std::nullptr_t) const {
|
|
||||||
return mLocalRef != nullptr;
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
JNIEnv* mEnv;
|
|
||||||
T mLocalRef;
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(ScopedLocalRef);
|
|
||||||
};
|
|
||||||
|
|
@ -20,10 +20,9 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <JNIHelper.h>
|
#include <jni_helper.h>
|
||||||
#include <base/object.h>
|
#include <base/object.h>
|
||||||
#include <art/runtime/mirror/class.h>
|
#include <art/runtime/mirror/class.h>
|
||||||
#include <android-base/strings.h>
|
|
||||||
#include "runtime.h"
|
#include "runtime.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "jni_env_ext.h"
|
#include "jni_env_ext.h"
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@
|
||||||
#pragma clang diagnostic push
|
#pragma clang diagnostic push
|
||||||
#pragma clang diagnostic ignored "-Wgnu-string-literal-operator-template"
|
#pragma clang diagnostic ignored "-Wgnu-string-literal-operator-template"
|
||||||
|
|
||||||
#include <art/base/macros.h>
|
#include "macros.h"
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "art/base/macros.h"
|
#include "macros.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
namespace lspd {
|
namespace lspd {
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
#include <art/base/macros.h>
|
#include "macros.h"
|
||||||
|
#include <string>
|
||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
|
|
||||||
#define JNI_START JNIEnv *env, [[maybe_unused]] jclass clazz
|
#define JNI_START JNIEnv *env, [[maybe_unused]] jclass clazz
|
||||||
|
|
@ -176,3 +177,55 @@ private:
|
||||||
|
|
||||||
JUTFString &operator=(const JUTFString &) = delete;
|
JUTFString &operator=(const JUTFString &) = delete;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class ScopedLocalRef {
|
||||||
|
public:
|
||||||
|
ScopedLocalRef(JNIEnv* env, T localRef) : mEnv(env), mLocalRef(localRef) {
|
||||||
|
}
|
||||||
|
ScopedLocalRef(ScopedLocalRef&& s) noexcept : mEnv(s.mEnv), mLocalRef(s.release()) {
|
||||||
|
}
|
||||||
|
explicit ScopedLocalRef(JNIEnv* env) : mEnv(env), mLocalRef(nullptr) {
|
||||||
|
}
|
||||||
|
~ScopedLocalRef() {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
void reset(T ptr = NULL) {
|
||||||
|
if (ptr != mLocalRef) {
|
||||||
|
if (mLocalRef != NULL) {
|
||||||
|
mEnv->DeleteLocalRef(mLocalRef);
|
||||||
|
}
|
||||||
|
mLocalRef = ptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
T release() __attribute__((warn_unused_result)) {
|
||||||
|
T localRef = mLocalRef;
|
||||||
|
mLocalRef = NULL;
|
||||||
|
return localRef;
|
||||||
|
}
|
||||||
|
T get() const {
|
||||||
|
return mLocalRef;
|
||||||
|
}
|
||||||
|
// We do not expose an empty constructor as it can easily lead to errors
|
||||||
|
// using common idioms, e.g.:
|
||||||
|
// ScopedLocalRef<...> ref;
|
||||||
|
// ref.reset(...);
|
||||||
|
// Move assignment operator.
|
||||||
|
ScopedLocalRef& operator=(ScopedLocalRef&& s) noexcept {
|
||||||
|
reset(s.release());
|
||||||
|
mEnv = s.mEnv;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
// Allows "if (scoped_ref == nullptr)"
|
||||||
|
bool operator==(std::nullptr_t) const {
|
||||||
|
return mLocalRef == nullptr;
|
||||||
|
}
|
||||||
|
// Allows "if (scoped_ref != nullptr)"
|
||||||
|
bool operator!=(std::nullptr_t) const {
|
||||||
|
return mLocalRef != nullptr;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
JNIEnv* mEnv;
|
||||||
|
T mLocalRef;
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(ScopedLocalRef);
|
||||||
|
};
|
||||||
|
|
@ -43,6 +43,7 @@
|
||||||
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
|
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
|
||||||
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
|
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
|
||||||
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
|
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
|
||||||
|
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL, LOG_TAG, __VA_ARGS__)
|
||||||
#define PLOGE(fmt, args...) LOGE(fmt " failed with %d: %s", ##args, errno, strerror(errno))
|
#define PLOGE(fmt, args...) LOGE(fmt " failed with %d: %s", ##args, errno, strerror(errno))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,33 +1,8 @@
|
||||||
/*
|
|
||||||
* 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
|
#pragma once
|
||||||
|
|
||||||
#include <stddef.h> // for size_t
|
#include <stddef.h> // for size_t
|
||||||
#include <unistd.h> // for TEMP_FAILURE_RETRY
|
#include <unistd.h> // for TEMP_FAILURE_RETRY
|
||||||
#include <utility>
|
#include <utility>
|
||||||
// bionic and glibc both have TEMP_FAILURE_RETRY, but eg Mac OS' libc doesn't.
|
|
||||||
#ifndef TEMP_FAILURE_RETRY
|
|
||||||
#define TEMP_FAILURE_RETRY(exp) \
|
|
||||||
({ \
|
|
||||||
decltype(exp) _rc; \
|
|
||||||
do { \
|
|
||||||
_rc = (exp); \
|
|
||||||
} while (_rc == -1 && errno == EINTR); \
|
|
||||||
_rc; \
|
|
||||||
})
|
|
||||||
#endif
|
|
||||||
// A macro to disallow the copy constructor and operator= functions
|
// A macro to disallow the copy constructor and operator= functions
|
||||||
// This must be placed in the private: declarations for a class.
|
// This must be placed in the private: declarations for a class.
|
||||||
//
|
//
|
||||||
|
|
@ -132,3 +107,52 @@ void UNUSED(const T&...) {
|
||||||
#elif defined(__mips__) && defined(__LP64__)
|
#elif defined(__mips__) && defined(__LP64__)
|
||||||
#define ABI_STRING "mips64"
|
#define ABI_STRING "mips64"
|
||||||
#endif
|
#endif
|
||||||
|
// A macro to disallow new and delete operators for a class. It goes in the private: declarations.
|
||||||
|
// NOTE: Providing placement new (and matching delete) for constructing container elements.
|
||||||
|
#define DISALLOW_ALLOCATION() \
|
||||||
|
public: \
|
||||||
|
NO_RETURN ALWAYS_INLINE void operator delete(void*, size_t) { UNREACHABLE(); } \
|
||||||
|
ALWAYS_INLINE void* operator new(size_t, void* ptr) noexcept { return ptr; } \
|
||||||
|
ALWAYS_INLINE void operator delete(void*, void*) noexcept { } \
|
||||||
|
private: \
|
||||||
|
void* operator new(size_t) = delete // NOLINT
|
||||||
|
#define ALIGNED(x) __attribute__ ((__aligned__(x)))
|
||||||
|
#define PACKED(x) __attribute__ ((__aligned__(x), __packed__))
|
||||||
|
// Stringify the argument.
|
||||||
|
#define QUOTE(x) #x
|
||||||
|
#define STRINGIFY(x) QUOTE(x)
|
||||||
|
// Append tokens after evaluating.
|
||||||
|
#define APPEND_TOKENS_AFTER_EVAL_2(a, b) a ## b
|
||||||
|
#define APPEND_TOKENS_AFTER_EVAL(a, b) APPEND_TOKENS_AFTER_EVAL_2(a, b)
|
||||||
|
#ifndef NDEBUG
|
||||||
|
#define ALWAYS_INLINE
|
||||||
|
#else
|
||||||
|
#define ALWAYS_INLINE __attribute__ ((always_inline))
|
||||||
|
#endif
|
||||||
|
// clang doesn't like attributes on lambda functions. It would be nice to say:
|
||||||
|
// #define ALWAYS_INLINE_LAMBDA ALWAYS_INLINE
|
||||||
|
#define ALWAYS_INLINE_LAMBDA
|
||||||
|
#define NO_INLINE __attribute__ ((noinline))
|
||||||
|
#if defined (__APPLE__)
|
||||||
|
#define HOT_ATTR
|
||||||
|
#define COLD_ATTR
|
||||||
|
#else
|
||||||
|
#define HOT_ATTR __attribute__ ((hot))
|
||||||
|
#define COLD_ATTR __attribute__ ((cold))
|
||||||
|
#endif
|
||||||
|
#define PURE __attribute__ ((__pure__))
|
||||||
|
// Define that a position within code is unreachable, for example:
|
||||||
|
// int foo () { LOG(FATAL) << "Don't call me"; UNREACHABLE(); }
|
||||||
|
// without the UNREACHABLE a return statement would be necessary.
|
||||||
|
#define UNREACHABLE __builtin_unreachable
|
||||||
|
// Add the C++11 noreturn attribute.
|
||||||
|
#define NO_RETURN [[ noreturn ]] // NOLINT[whitespace/braces] [5]
|
||||||
|
// Annotalysis thread-safety analysis support. Things that are not in base.
|
||||||
|
#define LOCKABLE CAPABILITY("mutex")
|
||||||
|
#define SHARED_LOCKABLE SHARED_CAPABILITY("mutex")
|
||||||
|
|
||||||
|
// DISALLOW_COPY_AND_ASSIGN disallows the copy and operator= functions. It goes in the private:
|
||||||
|
// declarations in a class.
|
||||||
|
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
|
||||||
|
TypeName(const TypeName&) = delete; \
|
||||||
|
void operator=(const TypeName&) = delete
|
||||||
|
|
@ -23,14 +23,14 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <context.h>
|
#include <context.h>
|
||||||
#include <art/base/macros.h>
|
#include "macros.h"
|
||||||
#include <nativehelper/scoped_local_ref.h>
|
#include "jni_helper.h"
|
||||||
#include <android-base/logging.h>
|
#include "logging.h"
|
||||||
#include "JNIHelper.h"
|
#include <cassert>
|
||||||
|
|
||||||
namespace lspd {
|
namespace lspd {
|
||||||
|
|
||||||
ALWAYS_INLINE inline void RegisterNativeMethodsInternal(JNIEnv *env,
|
ALWAYS_INLINE inline bool RegisterNativeMethodsInternal(JNIEnv *env,
|
||||||
const char *class_name,
|
const char *class_name,
|
||||||
const JNINativeMethod *methods,
|
const JNINativeMethod *methods,
|
||||||
jint method_count) {
|
jint method_count) {
|
||||||
|
|
@ -38,12 +38,31 @@ namespace lspd {
|
||||||
ScopedLocalRef<jclass> clazz(env,
|
ScopedLocalRef<jclass> clazz(env,
|
||||||
Context::GetInstance()->FindClassFromCurrentLoader(env, class_name));
|
Context::GetInstance()->FindClassFromCurrentLoader(env, class_name));
|
||||||
if (clazz.get() == nullptr) {
|
if (clazz.get() == nullptr) {
|
||||||
LOG(FATAL) << "Couldn't find class: " << class_name;
|
LOGF("Couldn't find class: %s", class_name);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
jint jni_result = JNI_RegisterNatives(env, clazz.get(), methods, method_count);
|
return JNI_RegisterNatives(env, clazz.get(), methods, method_count);
|
||||||
CHECK_EQ(JNI_OK, jni_result);
|
|
||||||
}
|
}
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
#define _NATIVEHELPER_JNI_MACRO_CAST(to) \
|
||||||
|
reinterpret_cast<to>
|
||||||
|
#else
|
||||||
|
#define _NATIVEHELPER_JNI_MACRO_CAST(to) \
|
||||||
|
(to)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LSP_NATIVE_METHOD
|
||||||
|
#define LSP_NATIVE_METHOD(className, functionName, signature) \
|
||||||
|
{ #functionName, \
|
||||||
|
signature, \
|
||||||
|
_NATIVEHELPER_JNI_MACRO_CAST(void*) (Java_io_github_lsposed_lspd_nativebridge_## className ## _ ## functionName) \
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LSP_DEF_NATIVE_METHOD
|
||||||
|
#define LSP_DEF_NATIVE_METHOD(ret, className, functionName, ...) \
|
||||||
|
extern "C" ret Java_io_github_lsposed_lspd_nativebridge_## className ## _ ## functionName (JNI_START, ## __VA_ARGS__)
|
||||||
|
#endif
|
||||||
|
|
||||||
#define REGISTER_LSP_NATIVE_METHODS(class_name) \
|
#define REGISTER_LSP_NATIVE_METHODS(class_name) \
|
||||||
RegisterNativeMethodsInternal(env, "io.github.lsposed.lspd.nativebridge." #class_name, gMethods, arraysize(gMethods))
|
RegisterNativeMethodsInternal(env, "io.github.lsposed.lspd.nativebridge." #class_name, gMethods, arraysize(gMethods))
|
||||||
|
|
|
||||||
|
|
@ -19,19 +19,16 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
#include <android-base/macros.h>
|
#include "jni_helper.h"
|
||||||
#include "JNIHelper.h"
|
|
||||||
#include "jni/art_class_linker.h"
|
#include "jni/art_class_linker.h"
|
||||||
#include "jni/yahfa.h"
|
#include "jni/yahfa.h"
|
||||||
#include "jni/sandhook.h"
|
#include "jni/sandhook.h"
|
||||||
#include "jni/resources_hook.h"
|
#include "jni/resources_hook.h"
|
||||||
#include <dl_util.h>
|
#include <dl_util.h>
|
||||||
#include <art/runtime/jni_env_ext.h>
|
#include <art/runtime/jni_env_ext.h>
|
||||||
#include <android-base/strings.h>
|
|
||||||
#include "jni/pending_hooks.h"
|
#include "jni/pending_hooks.h"
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <nativehelper/scoped_local_ref.h>
|
|
||||||
#include "context.h"
|
#include "context.h"
|
||||||
#include "native_hook.h"
|
#include "native_hook.h"
|
||||||
#include "jni/logger.h"
|
#include "jni/logger.h"
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
#include <native_util.h>
|
#include <native_util.h>
|
||||||
#include <art/runtime/class_linker.h>
|
#include <art/runtime/class_linker.h>
|
||||||
#include <nativehelper/jni_macros.h>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <HookMain.h>
|
#include <HookMain.h>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "nativehelper/jni_macros.h"
|
|
||||||
#include "native_util.h"
|
#include "native_util.h"
|
||||||
#include "JNIHelper.h"
|
#include "jni_helper.h"
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,9 +22,8 @@
|
||||||
// Created by 双草酸酯 on 2/7/21.
|
// Created by 双草酸酯 on 2/7/21.
|
||||||
//
|
//
|
||||||
#include "native_api.h"
|
#include "native_api.h"
|
||||||
#include "nativehelper/jni_macros.h"
|
|
||||||
#include "native_util.h"
|
#include "native_util.h"
|
||||||
#include "JNIHelper.h"
|
#include "jni_helper.h"
|
||||||
#include "../native_api.h"
|
#include "../native_api.h"
|
||||||
|
|
||||||
namespace lspd {
|
namespace lspd {
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@
|
||||||
* Copyright (C) 2021 LSPosed Contributors
|
* Copyright (C) 2021 LSPosed Contributors
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <nativehelper/jni_macros.h>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <shared_mutex>
|
#include <shared_mutex>
|
||||||
|
|
|
||||||
|
|
@ -19,18 +19,81 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
#include <resource_hook.h>
|
|
||||||
#include <dex_builder.h>
|
#include <dex_builder.h>
|
||||||
#include <art/runtime/thread.h>
|
#include <art/runtime/thread.h>
|
||||||
#include <art/runtime/mirror/class.h>
|
#include <art/runtime/mirror/class.h>
|
||||||
|
#include <dl_util.h>
|
||||||
|
#include <framework/androidfw/resource_types.h>
|
||||||
|
#include <byte_order.h>
|
||||||
#include "native_util.h"
|
#include "native_util.h"
|
||||||
#include "nativehelper/jni_macros.h"
|
|
||||||
#include "resources_hook.h"
|
#include "resources_hook.h"
|
||||||
|
|
||||||
namespace lspd {
|
namespace lspd {
|
||||||
|
static constexpr const char *kXResourcesClassName = "android/content/res/XResources";
|
||||||
|
|
||||||
|
typedef int32_t (*TYPE_GET_ATTR_NAME_ID)(void *, int);
|
||||||
|
|
||||||
|
typedef char16_t *(*TYPE_STRING_AT)(const void *, int32_t, size_t *);
|
||||||
|
|
||||||
|
typedef void(*TYPE_RESTART)(void *);
|
||||||
|
|
||||||
|
typedef int32_t (*TYPE_NEXT)(void *);
|
||||||
|
|
||||||
|
static jclass classXResources;
|
||||||
|
static jmethodID methodXResourcesTranslateAttrId;
|
||||||
|
static jmethodID methodXResourcesTranslateResId;
|
||||||
|
|
||||||
|
static TYPE_NEXT ResXMLParser_next = nullptr;
|
||||||
|
static TYPE_RESTART ResXMLParser_restart = nullptr;
|
||||||
|
static TYPE_GET_ATTR_NAME_ID ResXMLParser_getAttributeNameID = nullptr;
|
||||||
|
static TYPE_STRING_AT ResStringPool_stringAt = nullptr;
|
||||||
|
|
||||||
|
static bool PrepareSymbols() {
|
||||||
|
ScopedDlHandle fw_handle(kLibFwPath.c_str());
|
||||||
|
if (!fw_handle.IsValid()) {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
if (!(ResXMLParser_next = fw_handle.DlSym<TYPE_NEXT>(
|
||||||
|
"_ZN7android12ResXMLParser4nextEv"))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!(ResXMLParser_restart = fw_handle.DlSym<TYPE_RESTART>(
|
||||||
|
"_ZN7android12ResXMLParser7restartEv"))) {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
if (!(ResXMLParser_getAttributeNameID = fw_handle.DlSym<TYPE_GET_ATTR_NAME_ID>(
|
||||||
|
LP_SELECT("_ZNK7android12ResXMLParser18getAttributeNameIDEj",
|
||||||
|
"_ZNK7android12ResXMLParser18getAttributeNameIDEm")))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return (ResStringPool_stringAt = fw_handle.DlSym<TYPE_STRING_AT>(
|
||||||
|
LP_SELECT("_ZNK7android13ResStringPool8stringAtEjPj",
|
||||||
|
"_ZNK7android13ResStringPool8stringAtEmPm"))) != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
LSP_DEF_NATIVE_METHOD(jboolean, ResourcesHook, initXResourcesNative) {
|
LSP_DEF_NATIVE_METHOD(jboolean, ResourcesHook, initXResourcesNative) {
|
||||||
return XposedBridge_initXResourcesNative(env, clazz);
|
classXResources = Context::GetInstance()->FindClassFromCurrentLoader(env, kXResourcesClassName);
|
||||||
|
if (!classXResources) {
|
||||||
|
LOGE("Error while loading XResources class '%s':", kXResourcesClassName);
|
||||||
|
return JNI_FALSE;
|
||||||
|
}
|
||||||
|
methodXResourcesTranslateResId = JNI_GetStaticMethodID(
|
||||||
|
env, classXResources, "translateResId",
|
||||||
|
"(ILandroid/content/res/XResources;Landroid/content/res/Resources;)I");
|
||||||
|
if (!methodXResourcesTranslateResId) {
|
||||||
|
return JNI_FALSE;
|
||||||
|
}
|
||||||
|
methodXResourcesTranslateAttrId = JNI_GetStaticMethodID(
|
||||||
|
env, classXResources, "translateAttrId",
|
||||||
|
"(Ljava/lang/String;Landroid/content/res/XResources;)I");
|
||||||
|
if (!methodXResourcesTranslateAttrId) {
|
||||||
|
return JNI_FALSE;
|
||||||
|
}
|
||||||
|
if (!PrepareSymbols()) {
|
||||||
|
return JNI_FALSE;
|
||||||
|
}
|
||||||
|
classXResources = reinterpret_cast<jclass>(env->NewGlobalRef(classXResources));
|
||||||
|
return JNI_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ApiSensitive(Level.MIDDLE)
|
// @ApiSensitive(Level.MIDDLE)
|
||||||
|
|
@ -70,14 +133,87 @@ namespace lspd {
|
||||||
dex_buffer, parent);
|
dex_buffer, parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LSP_DEF_NATIVE_METHOD(void, ResourcesHook, rewriteXmlReferencesNative,
|
||||||
|
jlong parserPtr, jobject origRes, jobject repRes) {
|
||||||
|
auto parser = (android::ResXMLParser *) parserPtr;
|
||||||
|
|
||||||
|
if (parser == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const android::ResXMLTree &mTree = parser->mTree;
|
||||||
|
auto mResIds = (uint32_t *) mTree.mResIds;
|
||||||
|
android::ResXMLTree_attrExt *tag;
|
||||||
|
int attrCount;
|
||||||
|
|
||||||
|
do {
|
||||||
|
switch (ResXMLParser_next(parser)) {
|
||||||
|
case android::ResXMLParser::START_TAG:
|
||||||
|
tag = (android::ResXMLTree_attrExt *) parser->mCurExt;
|
||||||
|
attrCount = dtohs(tag->attributeCount);
|
||||||
|
for (int idx = 0; idx < attrCount; idx++) {
|
||||||
|
auto attr = (android::ResXMLTree_attribute *)
|
||||||
|
(((const uint8_t *) tag)
|
||||||
|
+ dtohs(tag->attributeStart)
|
||||||
|
+ (dtohs(tag->attributeSize) * idx));
|
||||||
|
|
||||||
|
// find resource IDs for attribute names
|
||||||
|
int32_t attrNameID = ResXMLParser_getAttributeNameID(parser, idx);
|
||||||
|
// only replace attribute name IDs for app packages
|
||||||
|
if (attrNameID >= 0 && (size_t) attrNameID < mTree.mNumResIds &&
|
||||||
|
dtohl(mResIds[attrNameID]) >= 0x7f000000) {
|
||||||
|
size_t attNameLen;
|
||||||
|
const char16_t *attrName = ResStringPool_stringAt(&(mTree.mStrings),
|
||||||
|
attrNameID,
|
||||||
|
&attNameLen);
|
||||||
|
jint attrResID = env->CallStaticIntMethod(classXResources,
|
||||||
|
methodXResourcesTranslateAttrId,
|
||||||
|
env->NewString(
|
||||||
|
(const jchar *) attrName,
|
||||||
|
attNameLen), origRes);
|
||||||
|
if (env->ExceptionCheck())
|
||||||
|
goto leave;
|
||||||
|
|
||||||
|
mResIds[attrNameID] = htodl(attrResID);
|
||||||
|
}
|
||||||
|
|
||||||
|
// find original resource IDs for reference values (app packages only)
|
||||||
|
if (attr->typedValue.dataType != android::Res_value::TYPE_REFERENCE)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
jint oldValue = dtohl(attr->typedValue.data);
|
||||||
|
if (oldValue < 0x7f000000)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
jint newValue = env->CallStaticIntMethod(classXResources,
|
||||||
|
methodXResourcesTranslateResId,
|
||||||
|
oldValue, origRes, repRes);
|
||||||
|
if (env->ExceptionCheck())
|
||||||
|
goto leave;
|
||||||
|
|
||||||
|
if (newValue != oldValue)
|
||||||
|
attr->typedValue.data = htodl(newValue);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
case android::ResXMLParser::END_DOCUMENT:
|
||||||
|
case android::ResXMLParser::BAD_DOCUMENT:
|
||||||
|
goto leave;
|
||||||
|
default:
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} while (true);
|
||||||
|
|
||||||
|
leave:
|
||||||
|
ResXMLParser_restart(parser);
|
||||||
|
}
|
||||||
|
|
||||||
static JNINativeMethod gMethods[] = {
|
static JNINativeMethod gMethods[] = {
|
||||||
LSP_NATIVE_METHOD(ResourcesHook, initXResourcesNative, "()Z"),
|
LSP_NATIVE_METHOD(ResourcesHook, initXResourcesNative, "()Z"),
|
||||||
LSP_NATIVE_METHOD(ResourcesHook, removeFinalFlagNative, "(Ljava/lang/Class;)Z"),
|
LSP_NATIVE_METHOD(ResourcesHook, removeFinalFlagNative, "(Ljava/lang/Class;)Z"),
|
||||||
LSP_NATIVE_METHOD(ResourcesHook, buildDummyClassLoader, "(Ljava/lang/ClassLoader;Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/ClassLoader;"),
|
LSP_NATIVE_METHOD(ResourcesHook, buildDummyClassLoader, "(Ljava/lang/ClassLoader;Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/ClassLoader;"),
|
||||||
|
LSP_NATIVE_METHOD(ResourcesHook, rewriteXmlReferencesNative,"(JLandroid/content/res/XResources;Landroid/content/res/Resources;)V")
|
||||||
};
|
};
|
||||||
|
|
||||||
void RegisterResourcesHook(JNIEnv *env) {
|
void RegisterResourcesHook(JNIEnv *env) {
|
||||||
REGISTER_LSP_NATIVE_METHODS(ResourcesHook);
|
REGISTER_LSP_NATIVE_METHODS(ResourcesHook);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -20,7 +20,6 @@
|
||||||
#include <sandhook.h>
|
#include <sandhook.h>
|
||||||
#include "sandhook.h"
|
#include "sandhook.h"
|
||||||
#include "native_util.h"
|
#include "native_util.h"
|
||||||
#include "nativehelper/jni_macros.h"
|
|
||||||
|
|
||||||
namespace lspd {
|
namespace lspd {
|
||||||
LSP_DEF_NATIVE_METHOD(bool, SandHook, init, jclass classSandHook, jclass classNeverCall) {
|
LSP_DEF_NATIVE_METHOD(bool, SandHook, init, jclass classSandHook, jclass classNeverCall) {
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@
|
||||||
|
|
||||||
#include "yahfa.h"
|
#include "yahfa.h"
|
||||||
#include "HookMain.h"
|
#include "HookMain.h"
|
||||||
#include "nativehelper/jni_macros.h"
|
|
||||||
#include "native_util.h"
|
#include "native_util.h"
|
||||||
#include "pending_hooks.h"
|
#include "pending_hooks.h"
|
||||||
#include "art/runtime/class_linker.h"
|
#include "art/runtime/class_linker.h"
|
||||||
|
|
|
||||||
|
|
@ -1,192 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of LSPosed.
|
|
||||||
*
|
|
||||||
* LSPosed is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* LSPosed is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 EdXposed Contributors
|
|
||||||
* Copyright (C) 2021 LSPosed Contributors
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <jni.h>
|
|
||||||
#include <ByteOrder.h>
|
|
||||||
#include <logging.h>
|
|
||||||
#include <dlfcn.h>
|
|
||||||
#include <android-base/macros.h>
|
|
||||||
#include <context.h>
|
|
||||||
#include <art/runtime/native/native_util.h>
|
|
||||||
#include <nativehelper/jni_macros.h>
|
|
||||||
#include <JNIHelper.h>
|
|
||||||
#include "framework/androidfw/ResourceTypes.h"
|
|
||||||
#include "resource_hook.h"
|
|
||||||
#include "dl_util.h"
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
// @ApiSensitive(Level.HIGH)
|
|
||||||
namespace lspd {
|
|
||||||
|
|
||||||
static constexpr const char *kXResourcesClassName = "android/content/res/XResources";
|
|
||||||
|
|
||||||
typedef int32_t (*TYPE_GET_ATTR_NAME_ID)(void *, int);
|
|
||||||
|
|
||||||
typedef char16_t *(*TYPE_STRING_AT)(const void *, int32_t, size_t *);
|
|
||||||
|
|
||||||
typedef void(*TYPE_RESTART)(void *);
|
|
||||||
|
|
||||||
typedef int32_t (*TYPE_NEXT)(void *);
|
|
||||||
|
|
||||||
static jclass classXResources;
|
|
||||||
static jmethodID methodXResourcesTranslateAttrId;
|
|
||||||
static jmethodID methodXResourcesTranslateResId;
|
|
||||||
|
|
||||||
static TYPE_NEXT ResXMLParser_next = nullptr;
|
|
||||||
static TYPE_RESTART ResXMLParser_restart = nullptr;
|
|
||||||
static TYPE_GET_ATTR_NAME_ID ResXMLParser_getAttributeNameID = nullptr;
|
|
||||||
static TYPE_STRING_AT ResStringPool_stringAt = nullptr;
|
|
||||||
|
|
||||||
static JNINativeMethod gMethods[] = {
|
|
||||||
NATIVE_METHOD(XResources, rewriteXmlReferencesNative,
|
|
||||||
"(JLandroid/content/res/XResources;Landroid/content/res/Resources;)V")
|
|
||||||
};
|
|
||||||
|
|
||||||
static bool register_natives_XResources(JNIEnv *env, jclass clazz) {
|
|
||||||
jint result = JNI_RegisterNatives(env, clazz, gMethods, arraysize(gMethods));
|
|
||||||
return result == JNI_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool PrepareSymbols() {
|
|
||||||
ScopedDlHandle fw_handle(kLibFwPath.c_str());
|
|
||||||
if (!fw_handle.IsValid()) {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
if (!(ResXMLParser_next = fw_handle.DlSym<TYPE_NEXT>(
|
|
||||||
"_ZN7android12ResXMLParser4nextEv"))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!(ResXMLParser_restart = fw_handle.DlSym<TYPE_RESTART>(
|
|
||||||
"_ZN7android12ResXMLParser7restartEv"))) {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
if (!(ResXMLParser_getAttributeNameID = fw_handle.DlSym<TYPE_GET_ATTR_NAME_ID>(
|
|
||||||
LP_SELECT("_ZNK7android12ResXMLParser18getAttributeNameIDEj",
|
|
||||||
"_ZNK7android12ResXMLParser18getAttributeNameIDEm")))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return (ResStringPool_stringAt = fw_handle.DlSym<TYPE_STRING_AT>(
|
|
||||||
LP_SELECT("_ZNK7android13ResStringPool8stringAtEjPj",
|
|
||||||
"_ZNK7android13ResStringPool8stringAtEmPm"))) != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
jboolean XposedBridge_initXResourcesNative(JNIEnv *env, jclass) {
|
|
||||||
classXResources = Context::GetInstance()->FindClassFromCurrentLoader(env, kXResourcesClassName);
|
|
||||||
if (!classXResources) {
|
|
||||||
LOGE("Error while loading XResources class '%s':", kXResourcesClassName);
|
|
||||||
return JNI_FALSE;
|
|
||||||
}
|
|
||||||
if (!register_natives_XResources(env, classXResources)) {
|
|
||||||
return JNI_FALSE;
|
|
||||||
}
|
|
||||||
methodXResourcesTranslateResId = JNI_GetStaticMethodID(
|
|
||||||
env, classXResources, "translateResId",
|
|
||||||
"(ILandroid/content/res/XResources;Landroid/content/res/Resources;)I");
|
|
||||||
if (!methodXResourcesTranslateResId) {
|
|
||||||
return JNI_FALSE;
|
|
||||||
}
|
|
||||||
methodXResourcesTranslateAttrId = JNI_GetStaticMethodID(
|
|
||||||
env, classXResources, "translateAttrId",
|
|
||||||
"(Ljava/lang/String;Landroid/content/res/XResources;)I");
|
|
||||||
if (!methodXResourcesTranslateAttrId) {
|
|
||||||
return JNI_FALSE;
|
|
||||||
}
|
|
||||||
if (!PrepareSymbols()) {
|
|
||||||
return JNI_FALSE;
|
|
||||||
}
|
|
||||||
classXResources = reinterpret_cast<jclass>(env->NewGlobalRef(classXResources));
|
|
||||||
return JNI_TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void XResources_rewriteXmlReferencesNative(JNIEnv *env, jclass,
|
|
||||||
jlong parserPtr, jobject origRes, jobject repRes) {
|
|
||||||
|
|
||||||
auto parser = (android::ResXMLParser *) parserPtr;
|
|
||||||
|
|
||||||
if (parser == nullptr)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const android::ResXMLTree &mTree = parser->mTree;
|
|
||||||
auto mResIds = (uint32_t *) mTree.mResIds;
|
|
||||||
android::ResXMLTree_attrExt *tag;
|
|
||||||
int attrCount;
|
|
||||||
|
|
||||||
do {
|
|
||||||
switch (ResXMLParser_next(parser)) {
|
|
||||||
case android::ResXMLParser::START_TAG:
|
|
||||||
tag = (android::ResXMLTree_attrExt *) parser->mCurExt;
|
|
||||||
attrCount = dtohs(tag->attributeCount);
|
|
||||||
for (int idx = 0; idx < attrCount; idx++) {
|
|
||||||
auto attr = (android::ResXMLTree_attribute *)
|
|
||||||
(((const uint8_t *) tag)
|
|
||||||
+ dtohs(tag->attributeStart)
|
|
||||||
+ (dtohs(tag->attributeSize) * idx));
|
|
||||||
|
|
||||||
// find resource IDs for attribute names
|
|
||||||
int32_t attrNameID = ResXMLParser_getAttributeNameID(parser, idx);
|
|
||||||
// only replace attribute name IDs for app packages
|
|
||||||
if (attrNameID >= 0 && (size_t) attrNameID < mTree.mNumResIds &&
|
|
||||||
dtohl(mResIds[attrNameID]) >= 0x7f000000) {
|
|
||||||
size_t attNameLen;
|
|
||||||
const char16_t *attrName = ResStringPool_stringAt(&(mTree.mStrings),
|
|
||||||
attrNameID,
|
|
||||||
&attNameLen);
|
|
||||||
jint attrResID = env->CallStaticIntMethod(classXResources,
|
|
||||||
methodXResourcesTranslateAttrId,
|
|
||||||
env->NewString(
|
|
||||||
(const jchar *) attrName,
|
|
||||||
attNameLen), origRes);
|
|
||||||
if (env->ExceptionCheck())
|
|
||||||
goto leave;
|
|
||||||
|
|
||||||
mResIds[attrNameID] = htodl(attrResID);
|
|
||||||
}
|
|
||||||
|
|
||||||
// find original resource IDs for reference values (app packages only)
|
|
||||||
if (attr->typedValue.dataType != android::Res_value::TYPE_REFERENCE)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
jint oldValue = dtohl(attr->typedValue.data);
|
|
||||||
if (oldValue < 0x7f000000)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
jint newValue = env->CallStaticIntMethod(classXResources,
|
|
||||||
methodXResourcesTranslateResId,
|
|
||||||
oldValue, origRes, repRes);
|
|
||||||
if (env->ExceptionCheck())
|
|
||||||
goto leave;
|
|
||||||
|
|
||||||
if (newValue != oldValue)
|
|
||||||
attr->typedValue.data = htodl(newValue);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
case android::ResXMLParser::END_DOCUMENT:
|
|
||||||
case android::ResXMLParser::BAD_DOCUMENT:
|
|
||||||
goto leave;
|
|
||||||
default:
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} while (true);
|
|
||||||
|
|
||||||
leave:
|
|
||||||
ResXMLParser_restart(parser);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of LSPosed.
|
|
||||||
*
|
|
||||||
* LSPosed is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* LSPosed is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 EdXposed Contributors
|
|
||||||
* Copyright (C) 2021 LSPosed Contributors
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
namespace lspd {
|
|
||||||
|
|
||||||
jboolean XposedBridge_initXResourcesNative(JNIEnv *env, jclass);
|
|
||||||
|
|
||||||
void XResources_rewriteXmlReferencesNative(JNIEnv *env, jclass,
|
|
||||||
jlong parserPtr, jobject origRes, jobject repRes);
|
|
||||||
}
|
|
||||||
|
|
@ -21,12 +21,11 @@
|
||||||
// Created by loves on 2/7/2021.
|
// Created by loves on 2/7/2021.
|
||||||
//
|
//
|
||||||
|
|
||||||
#include <nativehelper/scoped_local_ref.h>
|
|
||||||
#include <dobby.h>
|
#include <dobby.h>
|
||||||
#include "base/object.h"
|
#include "base/object.h"
|
||||||
#include "service.h"
|
#include "service.h"
|
||||||
#include "context.h"
|
#include "context.h"
|
||||||
#include "JNIHelper.h"
|
#include "jni_helper.h"
|
||||||
|
|
||||||
namespace lspd {
|
namespace lspd {
|
||||||
jboolean
|
jboolean
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@
|
||||||
|
|
||||||
#include "symbol_cache.h"
|
#include "symbol_cache.h"
|
||||||
#include <dobby.h>
|
#include <dobby.h>
|
||||||
#include <art/base/macros.h>
|
#include "macros.h"
|
||||||
#include <logging.h>
|
#include <logging.h>
|
||||||
|
|
||||||
namespace lspd {
|
namespace lspd {
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@ import static de.robv.android.xposed.XposedHelpers.getIntField;
|
||||||
import static de.robv.android.xposed.XposedHelpers.getLongField;
|
import static de.robv.android.xposed.XposedHelpers.getLongField;
|
||||||
import static de.robv.android.xposed.XposedHelpers.getObjectField;
|
import static de.robv.android.xposed.XposedHelpers.getObjectField;
|
||||||
import static de.robv.android.xposed.XposedHelpers.incrementMethodDepth;
|
import static de.robv.android.xposed.XposedHelpers.incrementMethodDepth;
|
||||||
|
import static io.github.lsposed.lspd.nativebridge.ResourcesHook.rewriteXmlReferencesNative;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link android.content.res.Resources} subclass that allows replacing individual resources.
|
* {@link android.content.res.Resources} subclass that allows replacing individual resources.
|
||||||
|
|
@ -1080,8 +1081,6 @@ public class XResources extends XResourcesSuperClass {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static native void rewriteXmlReferencesNative(long parserPtr, XResources origRes, Resources repRes);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to replace reference IDs in XMLs.
|
* Used to replace reference IDs in XMLs.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,9 @@
|
||||||
|
|
||||||
package io.github.lsposed.lspd.nativebridge;
|
package io.github.lsposed.lspd.nativebridge;
|
||||||
|
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.content.res.XResources;
|
||||||
|
|
||||||
public class ResourcesHook {
|
public class ResourcesHook {
|
||||||
|
|
||||||
public static native boolean initXResourcesNative();
|
public static native boolean initXResourcesNative();
|
||||||
|
|
@ -27,4 +30,6 @@ public class ResourcesHook {
|
||||||
public static native boolean removeFinalFlagNative(Class<?> clazz);
|
public static native boolean removeFinalFlagNative(Class<?> clazz);
|
||||||
|
|
||||||
public static native ClassLoader buildDummyClassLoader(ClassLoader parent, Class<?> resourceSuperClass, Class<?> typedArraySuperClass);
|
public static native ClassLoader buildDummyClassLoader(ClassLoader parent, Class<?> resourceSuperClass, Class<?> typedArraySuperClass);
|
||||||
|
|
||||||
|
public static native void rewriteXmlReferencesNative(long parserPtr, XResources origRes, Resources repRes);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue