This reverts commit 40e47762d7.
This commit is contained in:
parent
394d7fba19
commit
485da5a37c
|
|
@ -246,7 +246,6 @@ androidComponents.onVariants { v ->
|
||||||
val adb: String = androidComponents.sdkComponents.adb.get().asFile.absolutePath
|
val adb: String = androidComponents.sdkComponents.adb.get().asFile.absolutePath
|
||||||
val killLspd = task("killLspd", Exec::class) {
|
val killLspd = task("killLspd", Exec::class) {
|
||||||
commandLine(adb, "shell", "su", "-c", "killall -w lspd")
|
commandLine(adb, "shell", "su", "-c", "killall -w lspd")
|
||||||
isIgnoreExitValue = true
|
|
||||||
}
|
}
|
||||||
val pushLspd = task("pushLspd", Exec::class) {
|
val pushLspd = task("pushLspd", Exec::class) {
|
||||||
dependsOn("mergeDexDebug")
|
dependsOn("mergeDexDebug")
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
#!/sbin/sh
|
||||||
|
|
||||||
#################
|
#################
|
||||||
# Initialization
|
# Initialization
|
||||||
#################
|
#################
|
||||||
|
|
@ -21,9 +23,11 @@ require_new_magisk() {
|
||||||
OUTFD=$2
|
OUTFD=$2
|
||||||
ZIPFILE=$3
|
ZIPFILE=$3
|
||||||
|
|
||||||
|
mount /data 2>/dev/null
|
||||||
|
|
||||||
[ -f /data/adb/magisk/util_functions.sh ] || require_new_magisk
|
[ -f /data/adb/magisk/util_functions.sh ] || require_new_magisk
|
||||||
. /data/adb/magisk/util_functions.sh
|
. /data/adb/magisk/util_functions.sh
|
||||||
[ $MAGISK_VER_CODE -lt 20400 ] && require_new_magisk
|
[ $MAGISK_VER_CODE -lt 20400 ] && require_new_magisk
|
||||||
|
|
||||||
install_module
|
install_module
|
||||||
exit 0
|
exit 0
|
||||||
|
|
@ -120,4 +120,26 @@ fi
|
||||||
set_perm_recursive "$MODPATH" 0 0 0755 0644
|
set_perm_recursive "$MODPATH" 0 0 0755 0644
|
||||||
chmod 0744 "$MODPATH/lspd"
|
chmod 0744 "$MODPATH/lspd"
|
||||||
|
|
||||||
|
# Lsposed config
|
||||||
|
ui_print "- Creating configuration directories"
|
||||||
|
if [ -f /data/adb/lspd/misc_path ]; then
|
||||||
|
# read current MISC_PATH
|
||||||
|
MISC_PATH=$(cat /data/adb/lspd/misc_path)
|
||||||
|
ui_print " - Use previous path $MISC_PATH"
|
||||||
|
else
|
||||||
|
# generate random MISC_PATH
|
||||||
|
MISC_RAND=$(tr -cd 'A-Za-z0-9' </dev/urandom | head -c16)
|
||||||
|
MISC_PATH="lspd_${MISC_RAND}"
|
||||||
|
ui_print " - Use new path ${MISC_RAND}"
|
||||||
|
mkdir -p /data/adb/lspd || abort "! Can't create configuration path"
|
||||||
|
echo "$MISC_PATH" >/data/adb/lspd/misc_path || abort "! Can't store configuration path"
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "/data/misc/$MISC_PATH"
|
||||||
|
set_perm "/data/misc/$MISC_PATH" 0 0 0771 "u:object_r:magisk_file:s0" || abort "! Can't set permission"
|
||||||
|
echo "rm -rf /data/misc/$MISC_PATH" >>"${MODPATH}/uninstall.sh" || abort "! Can't write uninstall script"
|
||||||
|
|
||||||
|
[ -d /data/adb/lspd/config ] || mkdir -p /data/adb/lspd/config
|
||||||
|
[ -f /data/adb/lspd/config/verbose_log ] || echo "0" >/data/adb/lspd/config/verbose_log
|
||||||
|
|
||||||
ui_print "- Welcome to LSPosed!"
|
ui_print "- Welcome to LSPosed!"
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
#!/system/bin/sh
|
||||||
|
|
||||||
#
|
#
|
||||||
# This file is part of LSPosed.
|
# This file is part of LSPosed.
|
||||||
#
|
#
|
||||||
|
|
@ -20,5 +22,22 @@
|
||||||
|
|
||||||
MODDIR=${0%/*}
|
MODDIR=${0%/*}
|
||||||
|
|
||||||
|
MISC_PATH=$(cat /data/adb/lspd/misc_path)
|
||||||
|
BASE_PATH="/data/misc/$MISC_PATH"
|
||||||
|
|
||||||
|
LOG_PATH="/data/adb/lspd/log"
|
||||||
|
|
||||||
|
chcon -R u:object_r:system_file:s0 "${MODDIR}"
|
||||||
|
chcon -R u:object_r:system_file:s0 "/data/adb/lspd"
|
||||||
|
rm -rf ${LOG_PATH}.old
|
||||||
|
mv ${LOG_PATH} ${LOG_PATH}.old
|
||||||
|
mkdir -p ${LOG_PATH}
|
||||||
|
chcon -R u:object_r:magisk_file:s0 ${LOG_PATH}
|
||||||
|
|
||||||
|
if [ ! -z "${MISC_PATH}" ]; then
|
||||||
|
chcon -R u:object_r:magisk_file:s0 "${BASE_PATH}"
|
||||||
|
chmod 771 "${BASE_PATH}"
|
||||||
|
fi
|
||||||
|
|
||||||
rm -f "/data/local/tmp/lspd.dex"
|
rm -f "/data/local/tmp/lspd.dex"
|
||||||
unshare -m sh -c "$MODDIR/lspd &"
|
unshare -m sh -c "$MODDIR/lspd &"
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
#!/system/bin/sh
|
||||||
|
|
||||||
#
|
#
|
||||||
# This file is part of LSPosed.
|
# This file is part of LSPosed.
|
||||||
#
|
#
|
||||||
|
|
@ -19,4 +21,4 @@
|
||||||
|
|
||||||
MODDIR=${0%/*}
|
MODDIR=${0%/*}
|
||||||
# post-fs-data.sh may be blocked by other modules. retry to start this
|
# post-fs-data.sh may be blocked by other modules. retry to start this
|
||||||
unshare -m "$MODDIR/lspd" --from-service &
|
unshare -m $MODDIR/lspd --from-service &
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
#!/sbin/sh
|
||||||
|
|
||||||
#
|
#
|
||||||
# This file is part of LSPosed.
|
# This file is part of LSPosed.
|
||||||
#
|
#
|
||||||
|
|
@ -19,3 +21,4 @@
|
||||||
|
|
||||||
rm -rf /data/adb/lspd
|
rm -rf /data/adb/lspd
|
||||||
rm -rf /data/adb/riru/modules/lspd
|
rm -rf /data/adb/riru/modules/lspd
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
public static android.os.IBinder getApplicationServiceForSystemServer(android.os.IBinder, android.os.IBinder);
|
public static android.os.IBinder getApplicationServiceForSystemServer(android.os.IBinder, android.os.IBinder);
|
||||||
}
|
}
|
||||||
-keepclasseswithmembers class org.lsposed.lspd.service.LogcatService {
|
-keepclasseswithmembers class org.lsposed.lspd.service.LogcatService {
|
||||||
private int refreshFd(boolean);
|
private int refreshFd();
|
||||||
}
|
}
|
||||||
-assumenosideeffects class android.util.Log {
|
-assumenosideeffects class android.util.Log {
|
||||||
public static *** v(...);
|
public static *** v(...);
|
||||||
|
|
|
||||||
|
|
@ -27,20 +27,22 @@ public:
|
||||||
|
|
||||||
UniqueFile(int fd, const char *mode) : UniqueFile(fd > 0 ? fdopen(fd, mode) : stdout) {};
|
UniqueFile(int fd, const char *mode) : UniqueFile(fd > 0 ? fdopen(fd, mode) : stdout) {};
|
||||||
|
|
||||||
UniqueFile() : UniqueFile(stdout) {};
|
UniqueFile() : UniqueFile(nullptr) {};
|
||||||
};
|
};
|
||||||
|
|
||||||
class Logcat {
|
class Logcat {
|
||||||
public:
|
public:
|
||||||
explicit Logcat(JNIEnv *env, jobject thiz, jmethodID method, jlong logger_id) :
|
explicit Logcat(JNIEnv *env, jobject thiz, jmethodID method, jlong tid, jint fd,
|
||||||
env_(env), thiz_(thiz), refresh_fd_method_(method), logger_id_(logger_id),
|
jboolean verbose) :
|
||||||
stop_verbose_inst_("!!stop_verbose!!" + std::to_string(logger_id_)),
|
env_(env), thiz_(thiz), refresh_fd_method_(method), tid_(tid), module_file_(fd, "w"),
|
||||||
start_verbose_inst_("!!start_verbose!!" + std::to_string(logger_id_)) {}
|
verbose_(verbose),
|
||||||
|
stop_verbose_inst_("!!stop_verbose!!" + std::to_string(tid_)),
|
||||||
|
start_verbose_inst_("!!start_verbose!!" + std::to_string(tid_)) {}
|
||||||
|
|
||||||
[[noreturn]] void Run();
|
[[noreturn]] void Run();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
inline void RefreshFd(bool is_verbose);
|
inline void RefreshFd();
|
||||||
|
|
||||||
void ProcessBuffer(struct log_msg *buf);
|
void ProcessBuffer(struct log_msg *buf);
|
||||||
|
|
||||||
|
|
@ -49,17 +51,15 @@ private:
|
||||||
JNIEnv *env_;
|
JNIEnv *env_;
|
||||||
jobject thiz_;
|
jobject thiz_;
|
||||||
jmethodID refresh_fd_method_;
|
jmethodID refresh_fd_method_;
|
||||||
jlong logger_id_;
|
jlong tid_;
|
||||||
|
UniqueFile module_file_{};
|
||||||
|
size_t module_count_ = 0;
|
||||||
|
|
||||||
UniqueFile modules_file_{};
|
UniqueFile out_file_{};
|
||||||
size_t modules_file_part_ = 0;
|
size_t print_count_ = 0;
|
||||||
size_t modules_print_count_ = 0;
|
size_t file_count_ = 1;
|
||||||
|
|
||||||
UniqueFile verbose_file_{};
|
bool verbose_ = true;
|
||||||
size_t verbose_file_part_ = 0;
|
|
||||||
size_t verbose_print_count_ = 0;
|
|
||||||
|
|
||||||
bool verbose_ = false;
|
|
||||||
|
|
||||||
const std::string stop_verbose_inst_;
|
const std::string stop_verbose_inst_;
|
||||||
const std::string start_verbose_inst_;
|
const std::string start_verbose_inst_;
|
||||||
|
|
@ -91,22 +91,11 @@ int Logcat::PrintLogLine(const AndroidLogEntry &entry, FILE *out) {
|
||||||
entry.tag, static_cast<int>(message_len), message);
|
entry.tag, static_cast<int>(message_len), message);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logcat::RefreshFd(bool is_verbose) {
|
void Logcat::RefreshFd() {
|
||||||
constexpr const char *start_info = "----%" PRId64 "-%zu start----\n";
|
print_count_ = 0;
|
||||||
constexpr const char *stop_info = "----%" PRId64 "-%zu end----\n";
|
out_file_ = UniqueFile(env_->CallIntMethod(thiz_, refresh_fd_method_), "w");
|
||||||
if (is_verbose) {
|
fprintf(out_file_.get(), "%" PRId64 "-%zu\n", tid_, file_count_);
|
||||||
verbose_print_count_ = 0;
|
file_count_++;
|
||||||
fprintf(verbose_file_.get(), stop_info, logger_id_, verbose_file_part_);
|
|
||||||
verbose_file_ = UniqueFile(env_->CallIntMethod(thiz_, refresh_fd_method_, JNI_TRUE), "w");
|
|
||||||
verbose_file_part_++;
|
|
||||||
fprintf(verbose_file_.get(), start_info, logger_id_, verbose_file_part_);
|
|
||||||
} else {
|
|
||||||
modules_print_count_ = 0;
|
|
||||||
fprintf(modules_file_.get(), stop_info, logger_id_, modules_file_part_);
|
|
||||||
modules_file_ = UniqueFile(env_->CallIntMethod(thiz_, refresh_fd_method_, JNI_FALSE), "w");
|
|
||||||
modules_file_part_++;
|
|
||||||
fprintf(modules_file_.get(), start_info, logger_id_, modules_file_part_);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logcat::ProcessBuffer(struct log_msg *buf) {
|
void Logcat::ProcessBuffer(struct log_msg *buf) {
|
||||||
|
|
@ -114,31 +103,27 @@ void Logcat::ProcessBuffer(struct log_msg *buf) {
|
||||||
if (android_log_processLogBuffer(&buf->entry, &entry) < 0) return;
|
if (android_log_processLogBuffer(&buf->entry, &entry) < 0) return;
|
||||||
|
|
||||||
std::string_view tag(entry.tag);
|
std::string_view tag(entry.tag);
|
||||||
bool shortcut = false;
|
bool skip = false;
|
||||||
if (tag == "LSPosed-Bridge" || tag == "XSharedPreferences") [[unlikely]] {
|
if (tag == "LSPosed-Bridge" || tag == "XSharedPreferences") [[unlikely]] {
|
||||||
modules_print_count_ += PrintLogLine(entry, modules_file_.get());
|
module_count_ += PrintLogLine(entry, module_file_.get());
|
||||||
shortcut = true;
|
skip = true;
|
||||||
}
|
}
|
||||||
if (verbose_ && (shortcut || buf->id() == log_id::LOG_ID_CRASH ||
|
if (verbose_ && (skip || buf->id() == log_id::LOG_ID_CRASH ||
|
||||||
tag == "Magisk" ||
|
tag == "Magisk" ||
|
||||||
tag.starts_with("Riru") ||
|
tag.starts_with("Riru") ||
|
||||||
tag.starts_with("LSPosed"))) [[unlikely]] {
|
tag.starts_with("LSPosed"))) [[unlikely]] {
|
||||||
verbose_print_count_ += PrintLogLine(entry, verbose_file_.get());
|
print_count_ += PrintLogLine(entry, out_file_.get());
|
||||||
}
|
}
|
||||||
if (entry.pid == getpid() && tag == "LSPosedLogcat") [[unlikely]] {
|
if (entry.pid == getpid() && tag == "LSPosedLogcat") [[unlikely]] {
|
||||||
if (std::string_view(entry.message) == stop_verbose_inst_) {
|
if (std::string_view(entry.message) == stop_verbose_inst_) verbose_ = false;
|
||||||
verbose_ = false;
|
if (std::string_view(entry.message) == start_verbose_inst_) verbose_ = true;
|
||||||
} else if (std::string_view(entry.message) == start_verbose_inst_) {
|
|
||||||
RefreshFd(true);
|
|
||||||
verbose_ = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logcat::Run() {
|
void Logcat::Run() {
|
||||||
constexpr size_t tail_after_crash = 10U;
|
constexpr size_t tail_after_crash = 10U;
|
||||||
size_t tail = 0;
|
size_t tail = 0;
|
||||||
RefreshFd(false);
|
|
||||||
while (true) {
|
while (true) {
|
||||||
std::unique_ptr<logger_list, decltype(&android_logger_list_free)> logger_list{
|
std::unique_ptr<logger_list, decltype(&android_logger_list_free)> logger_list{
|
||||||
android_logger_list_alloc(0, tail, 0), &android_logger_list_free};
|
android_logger_list_alloc(0, tail, 0), &android_logger_list_free};
|
||||||
|
|
@ -150,6 +135,8 @@ void Logcat::Run() {
|
||||||
android_logger_set_log_size(logger, kMaxLogSize);
|
android_logger_set_log_size(logger, kMaxLogSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RefreshFd();
|
||||||
|
|
||||||
struct log_msg msg{};
|
struct log_msg msg{};
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
|
@ -157,14 +144,18 @@ void Logcat::Run() {
|
||||||
|
|
||||||
ProcessBuffer(&msg);
|
ProcessBuffer(&msg);
|
||||||
|
|
||||||
fflush(verbose_file_.get());
|
fflush(out_file_.get());
|
||||||
fflush(modules_file_.get());
|
fflush(module_file_.get());
|
||||||
|
|
||||||
if (verbose_print_count_ >= kMaxLogSize) [[unlikely]] RefreshFd(true);
|
if (print_count_ >= kMaxLogSize) [[unlikely]] RefreshFd();
|
||||||
if (modules_print_count_ >= kMaxLogSize) [[unlikely]] RefreshFd(false);
|
if (module_count_ >= kMaxLogSize) [[unlikely]] {
|
||||||
|
ftruncate(fileno(module_file_.get()), 0);
|
||||||
|
module_count_ = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fprintf(verbose_file_.get(), "\nLogd maybe crashed, retrying in 1s...\n");
|
fprintf(out_file_.get(),
|
||||||
fprintf(modules_file_.get(), "\nLogd maybe crashed, retrying in 1s...\n");
|
"\nLogd maybe crashed, retrying in %" PRId64 "-%zu file after 1s\n",
|
||||||
|
tid_, file_count_ + 1);
|
||||||
sleep(1);
|
sleep(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -172,9 +163,10 @@ void Logcat::Run() {
|
||||||
extern "C"
|
extern "C"
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
// NOLINTNEXTLINE
|
// NOLINTNEXTLINE
|
||||||
Java_org_lsposed_lspd_service_LogcatService_runLogcat(JNIEnv *env, jobject thiz, jlong logger_id) {
|
Java_org_lsposed_lspd_service_LogcatService_runLogcat(JNIEnv *env, jobject thiz, jlong tid,
|
||||||
|
jint fd, jboolean verbose) {
|
||||||
jclass clazz = env->GetObjectClass(thiz);
|
jclass clazz = env->GetObjectClass(thiz);
|
||||||
jmethodID method = env->GetMethodID(clazz, "refreshFd", "(Z)I");
|
jmethodID method = env->GetMethodID(clazz, "refreshFd", "()I");
|
||||||
Logcat logcat(env, thiz, method, logger_id);
|
Logcat logcat(env, thiz, method, tid, fd, verbose);
|
||||||
logcat.Run();
|
logcat.Run();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,152 +0,0 @@
|
||||||
package org.lsposed.lspd.service;
|
|
||||||
|
|
||||||
import static org.lsposed.lspd.service.ServiceManager.TAG;
|
|
||||||
|
|
||||||
import android.os.SELinux;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import org.lsposed.lspd.util.Utils;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.channels.FileChannel;
|
|
||||||
import java.nio.channels.FileLock;
|
|
||||||
import java.nio.file.FileVisitResult;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.OpenOption;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.SimpleFileVisitor;
|
|
||||||
import java.nio.file.StandardOpenOption;
|
|
||||||
import java.nio.file.attribute.BasicFileAttributes;
|
|
||||||
import java.nio.file.attribute.PosixFilePermissions;
|
|
||||||
import java.time.Instant;
|
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
import java.util.HashSet;
|
|
||||||
|
|
||||||
class ConfigFileManager {
|
|
||||||
static final File basePath = new File("/data/adb/lspd");
|
|
||||||
static final File managerApkPath = new File(basePath, "manager.apk");
|
|
||||||
private static final File lockPath = new File(basePath, "lock");
|
|
||||||
private static final File configDirPath = new File(basePath, "config");
|
|
||||||
static final File dbPath = new File(configDirPath, "modules_config.db");
|
|
||||||
private static final File logDirPath = new File(basePath, "log");
|
|
||||||
private static final File oldLogDirPath = new File(basePath, "log.old");
|
|
||||||
private static final DateTimeFormatter formatter =
|
|
||||||
DateTimeFormatter.ISO_LOCAL_DATE_TIME.withZone(Utils.getZoneId());
|
|
||||||
@SuppressWarnings("FieldCanBeLocal")
|
|
||||||
private static FileLocker locker = null;
|
|
||||||
|
|
||||||
static {
|
|
||||||
try {
|
|
||||||
Files.createDirectories(basePath.toPath());
|
|
||||||
SELinux.setFileContext(basePath.getPath(), "u:object_r:system_file:s0");
|
|
||||||
Files.createDirectories(configDirPath.toPath());
|
|
||||||
moveFolderIfExists(logDirPath.toPath(), oldLogDirPath.toPath());
|
|
||||||
Files.createDirectories(logDirPath.toPath());
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.e(TAG, Log.getStackTraceString(e));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getNewLogFileName(String prefix) {
|
|
||||||
return prefix + "-" + formatter.format(Instant.now()) + ".txt";
|
|
||||||
}
|
|
||||||
|
|
||||||
static File getNewVerboseLogPath() {
|
|
||||||
return new File(logDirPath, getNewLogFileName("verbose"));
|
|
||||||
}
|
|
||||||
|
|
||||||
static File getNewModulesLogPath() {
|
|
||||||
return new File(logDirPath, getNewLogFileName("modules"));
|
|
||||||
}
|
|
||||||
|
|
||||||
static boolean tryLock() {
|
|
||||||
var openOptions = new HashSet<OpenOption>();
|
|
||||||
openOptions.add(StandardOpenOption.CREATE);
|
|
||||||
openOptions.add(StandardOpenOption.WRITE);
|
|
||||||
var p = PosixFilePermissions.fromString("rw-------");
|
|
||||||
var permissions = PosixFilePermissions.asFileAttribute(p);
|
|
||||||
|
|
||||||
try {
|
|
||||||
var lockChannel = FileChannel.open(lockPath.toPath(), openOptions, permissions);
|
|
||||||
locker = new FileLocker(lockChannel);
|
|
||||||
return locker.isValid();
|
|
||||||
} catch (Throwable e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String readText(File file) throws IOException {
|
|
||||||
return new String(Files.readAllBytes(file.toPath())).trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void moveFolderIfExists(Path source, Path target) throws IOException {
|
|
||||||
if (!Files.exists(source)) return;
|
|
||||||
if (Files.exists(target)) {
|
|
||||||
Files.walkFileTree(target, new SimpleFileVisitor<>() {
|
|
||||||
@Override
|
|
||||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
|
|
||||||
throws IOException {
|
|
||||||
Files.delete(file);
|
|
||||||
return FileVisitResult.CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FileVisitResult postVisitDirectory(Path dir, IOException e)
|
|
||||||
throws IOException {
|
|
||||||
if (e == null) {
|
|
||||||
Files.delete(dir);
|
|
||||||
return FileVisitResult.CONTINUE;
|
|
||||||
} else {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Files.move(source, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Remove after next release
|
|
||||||
static void migrateOldConfig(ConfigManager configManager) {
|
|
||||||
var miscPath = new File(basePath, "misc_path");
|
|
||||||
var enableResources = new File(configDirPath, "enable_resources");
|
|
||||||
|
|
||||||
if (miscPath.exists()) {
|
|
||||||
try {
|
|
||||||
var s = readText(miscPath);
|
|
||||||
configManager.updateModulePrefs("lspd", 0, "config", "misc_path", s);
|
|
||||||
miscPath.delete();
|
|
||||||
} catch (IOException ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (enableResources.exists()) {
|
|
||||||
try {
|
|
||||||
var s = readText(enableResources);
|
|
||||||
var i = Integer.parseInt(s);
|
|
||||||
configManager.updateModulePrefs("lspd", 0, "config", "enable_resources", i == 1);
|
|
||||||
enableResources.delete();
|
|
||||||
} catch (IOException ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class FileLocker {
|
|
||||||
private final FileChannel lockChannel;
|
|
||||||
private final FileLock locker;
|
|
||||||
|
|
||||||
FileLocker(FileChannel lockChannel) throws IOException {
|
|
||||||
this.lockChannel = lockChannel;
|
|
||||||
this.locker = lockChannel.tryLock();
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isValid() {
|
|
||||||
return this.locker != null && this.locker.isValid();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void finalize() throws Throwable {
|
|
||||||
this.locker.release();
|
|
||||||
this.lockChannel.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -23,6 +23,7 @@ import static org.lsposed.lspd.service.PackageService.MATCH_ALL_FLAGS;
|
||||||
import static org.lsposed.lspd.service.PackageService.PER_USER_RANGE;
|
import static org.lsposed.lspd.service.PackageService.PER_USER_RANGE;
|
||||||
import static org.lsposed.lspd.service.ServiceManager.TAG;
|
import static org.lsposed.lspd.service.ServiceManager.TAG;
|
||||||
import static org.lsposed.lspd.service.ServiceManager.existsInGlobalNamespace;
|
import static org.lsposed.lspd.service.ServiceManager.existsInGlobalNamespace;
|
||||||
|
import static org.lsposed.lspd.service.ServiceManager.getLogcatService;
|
||||||
import static org.lsposed.lspd.service.ServiceManager.toGlobalNamespace;
|
import static org.lsposed.lspd.service.ServiceManager.toGlobalNamespace;
|
||||||
|
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
|
|
@ -62,8 +63,12 @@ import java.io.InputStreamReader;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.nio.channels.Channels;
|
import java.nio.channels.Channels;
|
||||||
|
import java.nio.channels.FileChannel;
|
||||||
|
import java.nio.channels.FileLock;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.OpenOption;
|
||||||
|
import java.nio.file.StandardOpenOption;
|
||||||
|
import java.nio.file.attribute.PosixFilePermissions;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
@ -74,24 +79,65 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.zip.ZipFile;
|
import java.util.zip.ZipFile;
|
||||||
|
|
||||||
|
// This config manager assume uid won't change when our service is off.
|
||||||
|
// Otherwise, user should maintain it manually.
|
||||||
public class ConfigManager {
|
public class ConfigManager {
|
||||||
private static ConfigManager instance = null;
|
private static ConfigManager instance = null;
|
||||||
|
|
||||||
private final SQLiteDatabase db =
|
private static final File basePath = new File("/data/adb/lspd");
|
||||||
SQLiteDatabase.openOrCreateDatabase(ConfigFileManager.dbPath, null);
|
private static final File configPath = new File(basePath, "config");
|
||||||
|
private static final File lockPath = new File(basePath, "lock");
|
||||||
|
private static final SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(new File(configPath, "modules_config.db"), null);
|
||||||
|
|
||||||
private boolean packageStarted = false;
|
private boolean packageStarted = false;
|
||||||
|
|
||||||
|
private static final File resourceHookSwitch = new File(configPath, "enable_resources");
|
||||||
private boolean resourceHook = false;
|
private boolean resourceHook = false;
|
||||||
private boolean verboseLog = true;
|
|
||||||
|
private boolean logcat = true;
|
||||||
|
|
||||||
|
private static final File managerPath = new File(configPath, "manager");
|
||||||
|
private String manager = null;
|
||||||
|
private int managerUid = -1;
|
||||||
|
|
||||||
|
private static final File miscFile = new File(basePath, "misc_path");
|
||||||
private String miscPath = null;
|
private String miscPath = null;
|
||||||
|
|
||||||
private final String manager = BuildConfig.DEFAULT_MANAGER_PACKAGE_NAME;
|
private static final File logPath = new File(basePath, "log");
|
||||||
private int managerUid = -1;
|
|
||||||
|
static class FileLocker {
|
||||||
|
private final FileChannel lockChannel;
|
||||||
|
private final FileLock locker;
|
||||||
|
|
||||||
|
FileLocker(@NonNull FileChannel lockChannel) throws IOException {
|
||||||
|
this.lockChannel = lockChannel;
|
||||||
|
this.locker = lockChannel.tryLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isValid() {
|
||||||
|
return this.locker != null && this.locker.isValid();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void finalize() throws Throwable {
|
||||||
|
this.locker.release();
|
||||||
|
this.lockChannel.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static FileLocker locker = null;
|
||||||
|
|
||||||
|
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
Files.createDirectories(logPath.toPath());
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, Log.getStackTraceString(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private final Handler cacheHandler;
|
private final Handler cacheHandler;
|
||||||
|
|
||||||
|
|
@ -127,20 +173,20 @@ public class ConfigManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final SQLiteStatement createModulesTable = db.compileStatement("CREATE TABLE IF NOT EXISTS modules (" +
|
private static final SQLiteStatement createModulesTable = db.compileStatement("CREATE TABLE IF NOT EXISTS modules (" +
|
||||||
"mid integer PRIMARY KEY AUTOINCREMENT," +
|
"mid integer PRIMARY KEY AUTOINCREMENT," +
|
||||||
"module_pkg_name text NOT NULL UNIQUE," +
|
"module_pkg_name text NOT NULL UNIQUE," +
|
||||||
"apk_path text NOT NULL, " +
|
"apk_path text NOT NULL, " +
|
||||||
"enabled BOOLEAN DEFAULT 0 " +
|
"enabled BOOLEAN DEFAULT 0 " +
|
||||||
"CHECK (enabled IN (0, 1))" +
|
"CHECK (enabled IN (0, 1))" +
|
||||||
");");
|
");");
|
||||||
private final SQLiteStatement createScopeTable = db.compileStatement("CREATE TABLE IF NOT EXISTS scope (" +
|
private static final SQLiteStatement createScopeTable = db.compileStatement("CREATE TABLE IF NOT EXISTS scope (" +
|
||||||
"mid integer," +
|
"mid integer," +
|
||||||
"app_pkg_name text NOT NULL," +
|
"app_pkg_name text NOT NULL," +
|
||||||
"user_id integer NOT NULL," +
|
"user_id integer NOT NULL," +
|
||||||
"PRIMARY KEY (mid, app_pkg_name, user_id)" +
|
"PRIMARY KEY (mid, app_pkg_name, user_id)" +
|
||||||
");");
|
");");
|
||||||
private final SQLiteStatement createConfigTable = db.compileStatement("CREATE TABLE IF NOT EXISTS config (" +
|
private static final SQLiteStatement createConfigTable = db.compileStatement("CREATE TABLE IF NOT EXISTS config (" +
|
||||||
"module_pkg_name text NOT NULL," +
|
"module_pkg_name text NOT NULL," +
|
||||||
"user_id integer NOT NULL," +
|
"user_id integer NOT NULL," +
|
||||||
"`group` text NOT NULL," +
|
"`group` text NOT NULL," +
|
||||||
|
|
@ -168,6 +214,22 @@ public class ConfigManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean tryLock() {
|
||||||
|
var openOptions = new HashSet<OpenOption>();
|
||||||
|
openOptions.add(StandardOpenOption.CREATE);
|
||||||
|
openOptions.add(StandardOpenOption.WRITE);
|
||||||
|
var p = PosixFilePermissions.fromString("rw-------");
|
||||||
|
var permissions = PosixFilePermissions.asFileAttribute(p);
|
||||||
|
|
||||||
|
try {
|
||||||
|
var lockChannel = FileChannel.open(lockPath.toPath(), openOptions, permissions);
|
||||||
|
locker = new FileLocker(lockChannel);
|
||||||
|
return locker.isValid();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// for system server, cache is not yet ready, we need to query database for it
|
// for system server, cache is not yet ready, we need to query database for it
|
||||||
public boolean shouldSkipSystemServer() {
|
public boolean shouldSkipSystemServer() {
|
||||||
if (!SELinux.checkSELinuxAccess("u:r:system_server:s0", "u:r:system_server:s0", "process", "execmem")) {
|
if (!SELinux.checkSELinuxAccess("u:r:system_server:s0", "u:r:system_server:s0", "process", "execmem")) {
|
||||||
|
|
@ -207,40 +269,55 @@ public class ConfigManager {
|
||||||
return modules;
|
return modules;
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void updateConfig() {
|
private static String readText(@NonNull File file) throws IOException {
|
||||||
ConfigFileManager.migrateOldConfig(this);
|
return new String(Files.readAllBytes(file.toPath())).trim();
|
||||||
Map<String, Object> config = getModulePrefs("lspd", 0, "config");
|
}
|
||||||
|
|
||||||
Object bool = config.get("enable_resources");
|
private static String readText(@NonNull File file, String defaultValue) {
|
||||||
resourceHook = bool != null && (boolean) bool;
|
|
||||||
|
|
||||||
bool = config.get("enable_verbose_log");
|
|
||||||
verboseLog = bool == null || (boolean) bool;
|
|
||||||
|
|
||||||
// Don't migrate to ConfigFileManager, as XSharedPreferences will be restored soon
|
|
||||||
String string = (String) config.get("misc_path");
|
|
||||||
if (string == null) {
|
|
||||||
miscPath = "/data/misc/" + UUID.randomUUID().toString();
|
|
||||||
updateModulePrefs("lspd", 0, "config", "misc_path", miscPath);
|
|
||||||
} else {
|
|
||||||
miscPath = string;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
Files.createDirectories(Paths.get(miscPath));
|
if (!file.exists()) return defaultValue;
|
||||||
SELinux.setFileContext(miscPath, "u:object_r:magisk_file:s0");
|
return readText(file);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.e(TAG, Log.getStackTraceString(e));
|
Log.e(TAG, Log.getStackTraceString(e));
|
||||||
}
|
}
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void writeText(@NonNull File file, String value) {
|
||||||
|
try {
|
||||||
|
Files.write(file.toPath(), value.getBytes(), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, Log.getStackTraceString(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int readInt(@NonNull File file, int defaultValue) {
|
||||||
|
try {
|
||||||
|
if (!file.exists()) return defaultValue;
|
||||||
|
return Integer.parseInt(readText(file));
|
||||||
|
} catch (IOException | NumberFormatException e) {
|
||||||
|
Log.e(TAG, Log.getStackTraceString(e));
|
||||||
|
}
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void writeInt(@NonNull File file, int value) {
|
||||||
|
writeText(file, String.valueOf(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void updateConfig() {
|
||||||
|
resourceHook = readInt(resourceHookSwitch, 0) == 1;
|
||||||
|
miscPath = "/data/misc/" + readText(miscFile, "lspd");
|
||||||
updateManager();
|
updateManager();
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void updateManager() {
|
public synchronized void updateManager() {
|
||||||
if (!packageStarted) return;
|
if (!packageStarted) return;
|
||||||
try {
|
try {
|
||||||
PackageInfo info = PackageService.getPackageInfo(manager, 0, 0);
|
PackageInfo info = PackageService.getPackageInfo(readText(managerPath, BuildConfig.DEFAULT_MANAGER_PACKAGE_NAME), 0, 0);
|
||||||
if (info != null) {
|
if (info != null) {
|
||||||
managerUid = info.applicationInfo.uid;
|
managerUid = info.applicationInfo.uid;
|
||||||
|
manager = info.packageName;
|
||||||
} else {
|
} else {
|
||||||
Log.w(TAG, "manager is not installed");
|
Log.w(TAG, "manager is not installed");
|
||||||
}
|
}
|
||||||
|
|
@ -251,12 +328,19 @@ public class ConfigManager {
|
||||||
public void ensureManager() {
|
public void ensureManager() {
|
||||||
if (!packageStarted) return;
|
if (!packageStarted) return;
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
if (PackageService.installManagerIfAbsent(manager, ConfigFileManager.managerApkPath)) {
|
if (PackageService.installManagerIfAbsent(manager, new File(basePath, "manager.apk"))) {
|
||||||
updateManager();
|
updateManager(BuildConfig.DEFAULT_MANAGER_PACKAGE_NAME);
|
||||||
}
|
}
|
||||||
}).start();
|
}).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public synchronized void updateManager(@NonNull String packageName) {
|
||||||
|
Log.i(TAG, "Now manager is " + packageName);
|
||||||
|
writeText(managerPath, packageName);
|
||||||
|
manager = packageName;
|
||||||
|
updateManager();
|
||||||
|
}
|
||||||
|
|
||||||
static ConfigManager getInstance() {
|
static ConfigManager getInstance() {
|
||||||
if (instance == null)
|
if (instance == null)
|
||||||
instance = new ConfigManager();
|
instance = new ConfigManager();
|
||||||
|
|
@ -302,7 +386,7 @@ public class ConfigManager {
|
||||||
Map<String, ConcurrentHashMap<String, Object>> fetchModuleConfig(String name, int user_id) {
|
Map<String, ConcurrentHashMap<String, Object>> fetchModuleConfig(String name, int user_id) {
|
||||||
var config = new ConcurrentHashMap<String, ConcurrentHashMap<String, Object>>();
|
var config = new ConcurrentHashMap<String, ConcurrentHashMap<String, Object>>();
|
||||||
|
|
||||||
try (Cursor cursor = db.query("config", new String[]{"`group`", "`key`", "data"},
|
try (Cursor cursor = db.query("config", new String[]{"group", "key", "data"},
|
||||||
"module_pkg_name = ? and user_id = ?", new String[]{name, String.valueOf(user_id)}, null, null, null)) {
|
"module_pkg_name = ? and user_id = ?", new String[]{name, String.valueOf(user_id)}, null, null, null)) {
|
||||||
if (cursor == null) {
|
if (cursor == null) {
|
||||||
Log.e(TAG, "db cache failed");
|
Log.e(TAG, "db cache failed");
|
||||||
|
|
@ -329,21 +413,19 @@ public class ConfigManager {
|
||||||
if (value instanceof Serializable) {
|
if (value instanceof Serializable) {
|
||||||
prefs.put(key, value);
|
prefs.put(key, value);
|
||||||
var values = new ContentValues();
|
var values = new ContentValues();
|
||||||
values.put("`group`", group);
|
values.put("group", group);
|
||||||
values.put("`key`", key);
|
values.put("key", key);
|
||||||
values.put("data", SerializationUtils.serialize((Serializable) value));
|
values.put("value", SerializationUtils.serialize((Serializable) value));
|
||||||
values.put("module_pkg_name", moduleName);
|
db.updateWithOnConflict("config", values, "module_pkg_name=? and user_id=?", new String[]{moduleName, String.valueOf(userId)}, SQLiteDatabase.CONFLICT_REPLACE);
|
||||||
values.put("user_id", String.valueOf(userId));
|
|
||||||
db.insertWithOnConflict("config", null, values, SQLiteDatabase.CONFLICT_REPLACE);
|
|
||||||
} else {
|
} else {
|
||||||
prefs.remove(key);
|
prefs.remove(key);
|
||||||
db.delete("config", "module_pkg_name=? and user_id=? and `group`=? and `key`=?", new String[]{moduleName, String.valueOf(userId), group, key});
|
db.delete("config", "module_pkg_name=? and user_id=?", new String[]{moduleName, String.valueOf(userId)});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConcurrentHashMap<String, Object> getModulePrefs(String moduleName, int userId, String group) {
|
public ConcurrentHashMap<String, Object> getModulePrefs(String moduleName, int userId, String group) {
|
||||||
var config = cachedConfig.computeIfAbsent(new Pair<>(moduleName, userId), module -> fetchModuleConfig(module.first, module.second));
|
var config = cachedConfig.computeIfAbsent(new Pair<>(moduleName, userId), module -> fetchModuleConfig(module.first, module.second));
|
||||||
return config.getOrDefault(group, new ConcurrentHashMap<>());
|
return config.getOrDefault(group, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void cacheModules() {
|
private synchronized void cacheModules() {
|
||||||
|
|
@ -621,11 +703,9 @@ public class ConfigManager {
|
||||||
Log.w(TAG, "update module apk path should not be called inside transaction");
|
Log.w(TAG, "update module apk path should not be called inside transaction");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
values.put("module_pkg_name", packageName);
|
values.put("module_pkg_name", packageName);
|
||||||
values.put("apk_path", apkPath);
|
values.put("apk_path", apkPath);
|
||||||
// insert or update in two step since insert or replace will change the autoincrement mid
|
|
||||||
int count = (int) db.insertWithOnConflict("modules", null, values, SQLiteDatabase.CONFLICT_IGNORE);
|
int count = (int) db.insertWithOnConflict("modules", null, values, SQLiteDatabase.CONFLICT_IGNORE);
|
||||||
if (count < 0) {
|
if (count < 0) {
|
||||||
count = db.updateWithOnConflict("modules", values, "module_pkg_name=?", new String[]{packageName}, SQLiteDatabase.CONFLICT_IGNORE);
|
count = db.updateWithOnConflict("modules", values, "module_pkg_name=?", new String[]{packageName}, SQLiteDatabase.CONFLICT_IGNORE);
|
||||||
|
|
@ -782,19 +862,18 @@ public class ConfigManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setResourceHook(boolean resourceHook) {
|
public void setResourceHook(boolean resourceHook) {
|
||||||
updateModulePrefs("lspd", 0, "config", "enable_resources", resourceHook);
|
writeInt(resourceHookSwitch, resourceHook ? 1 : 0);
|
||||||
this.resourceHook = resourceHook;
|
this.resourceHook = resourceHook;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setVerboseLog(boolean on) {
|
public void setVerboseLog(boolean on) {
|
||||||
var logcatService = ServiceManager.getLogcatService();
|
var logcatService = ServiceManager.getLogcatService();
|
||||||
if (on) {
|
if (on) {
|
||||||
logcatService.startVerbose();
|
logcatService.start();
|
||||||
} else {
|
} else {
|
||||||
logcatService.stopVerbose();
|
logcatService.stop();
|
||||||
}
|
}
|
||||||
updateModulePrefs("lspd", 0, "config", "enable_verbose_log", on);
|
logcat = on;
|
||||||
verboseLog = on;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean resourceHook() {
|
public boolean resourceHook() {
|
||||||
|
|
@ -802,14 +881,17 @@ public class ConfigManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean verboseLog() {
|
public boolean verboseLog() {
|
||||||
return verboseLog;
|
return logcat;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ParcelFileDescriptor getModulesLog() {
|
public static File getLogPath() {
|
||||||
|
return logPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ParcelFileDescriptor getModulesLog(int mode) {
|
||||||
|
var modulesLog = getLogcatService().getModulesLog();
|
||||||
try {
|
try {
|
||||||
var modulesLog = ServiceManager.getLogcatService().getModulesLog();
|
return ParcelFileDescriptor.open(modulesLog, mode);
|
||||||
if (modulesLog == null) return null;
|
|
||||||
return ParcelFileDescriptor.open(modulesLog, ParcelFileDescriptor.MODE_READ_ONLY);
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.e(TAG, Log.getStackTraceString(e));
|
Log.e(TAG, Log.getStackTraceString(e));
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -818,9 +900,9 @@ public class ConfigManager {
|
||||||
|
|
||||||
public ParcelFileDescriptor getVerboseLog() {
|
public ParcelFileDescriptor getVerboseLog() {
|
||||||
try {
|
try {
|
||||||
var verboseLog = ServiceManager.getLogcatService().getVerboseLog();
|
var logcat = ServiceManager.getLogcatService().getLog();
|
||||||
if (verboseLog == null) return null;
|
if (logcat == null) return null;
|
||||||
return ParcelFileDescriptor.open(verboseLog, ParcelFileDescriptor.MODE_READ_ONLY);
|
return ParcelFileDescriptor.open(logcat, ParcelFileDescriptor.MODE_READ_ONLY);
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
Log.e(TAG, Log.getStackTraceString(e));
|
Log.e(TAG, Log.getStackTraceString(e));
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -828,11 +910,11 @@ public class ConfigManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean clearLogs(boolean verbose) {
|
public boolean clearLogs(boolean verbose) {
|
||||||
var logcatService = ServiceManager.getLogcatService();
|
|
||||||
File logFile = verbose ? logcatService.getVerboseLog() : logcatService.getModulesLog();
|
|
||||||
if (logFile == null) return true;
|
|
||||||
try {
|
try {
|
||||||
OutputStream os = new FileOutputStream(logFile);
|
var logcat = ServiceManager.getLogcatService().getLog();
|
||||||
|
var moduleLog = ServiceManager.getLogcatService().getModulesLog();
|
||||||
|
if (verbose && logcat == null) return true;
|
||||||
|
OutputStream os = new FileOutputStream(verbose ? logcat : moduleLog);
|
||||||
os.close();
|
os.close();
|
||||||
return true;
|
return true;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
|
@ -842,7 +924,7 @@ public class ConfigManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isManager(String packageName) {
|
public boolean isManager(String packageName) {
|
||||||
return packageName.equals(manager);
|
return packageName.equals(manager) || packageName.equals(BuildConfig.DEFAULT_MANAGER_PACKAGE_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isManager(int uid) {
|
public boolean isManager(int uid) {
|
||||||
|
|
@ -855,7 +937,7 @@ public class ConfigManager {
|
||||||
|
|
||||||
public String getPrefsPath(String fileName, int uid) {
|
public String getPrefsPath(String fileName, int uid) {
|
||||||
int userId = uid / PER_USER_RANGE;
|
int userId = uid / PER_USER_RANGE;
|
||||||
return miscPath + "/prefs" + (userId == 0 ? "" : String.valueOf(userId)) + "/" + fileName;
|
return miscPath + File.separator + "prefs" + (userId == 0 ? "" : String.valueOf(userId)) + File.separator + fileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is slow, avoid using it
|
// this is slow, avoid using it
|
||||||
|
|
|
||||||
|
|
@ -176,7 +176,7 @@ public class LSPManagerService extends ILSPManagerService.Stub {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ParcelFileDescriptor getModulesLog() {
|
public ParcelFileDescriptor getModulesLog() {
|
||||||
return ConfigManager.getInstance().getModulesLog();
|
return ConfigManager.getInstance().getModulesLog(ParcelFileDescriptor.MODE_READ_ONLY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -4,46 +4,55 @@ import android.annotation.SuppressLint;
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import org.lsposed.lspd.util.Utils;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
|
||||||
public class LogcatService implements Runnable {
|
public class LogcatService implements Runnable {
|
||||||
private static final String TAG = "LSPosedLogcat";
|
private static final String TAG = "LSPosedLogcat";
|
||||||
private static final int mode = ParcelFileDescriptor.MODE_WRITE_ONLY |
|
private static final int mode = ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_CREATE |
|
||||||
ParcelFileDescriptor.MODE_CREATE |
|
ParcelFileDescriptor.MODE_TRUNCATE | ParcelFileDescriptor.MODE_APPEND;
|
||||||
ParcelFileDescriptor.MODE_TRUNCATE |
|
public final File modulesLog;
|
||||||
ParcelFileDescriptor.MODE_APPEND;
|
private final File logPath;
|
||||||
private File modulesLog = null;
|
private final DateTimeFormatter logTimeFormat;
|
||||||
private File verboseLog = null;
|
private File log = null;
|
||||||
private Thread thread = null;
|
private Thread thread = null;
|
||||||
|
boolean verboseLog = true;
|
||||||
|
|
||||||
@SuppressLint("UnsafeDynamicallyLoadedCode")
|
@SuppressLint("UnsafeDynamicallyLoadedCode")
|
||||||
public LogcatService() {
|
public LogcatService(File logPath) {
|
||||||
String libraryPath = System.getProperty("lsp.library.path");
|
String libraryPath = System.getProperty("lsp.library.path");
|
||||||
System.load(libraryPath + "/" + System.mapLibraryName("daemon"));
|
System.load(libraryPath + "/" + System.mapLibraryName("daemon"));
|
||||||
|
this.logPath = logPath;
|
||||||
|
modulesLog = new File(logPath, "module.log");
|
||||||
|
logTimeFormat = DateTimeFormatter.ISO_LOCAL_DATE_TIME.withZone(Utils.getZoneId());
|
||||||
}
|
}
|
||||||
|
|
||||||
private native void runLogcat(long loggerId);
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
Log.i(TAG, "start running");
|
Log.i(TAG, "start running");
|
||||||
runLogcat(thread.getId());
|
int moduleFd = -1;
|
||||||
|
try (var fd = ParcelFileDescriptor.open(modulesLog, mode)) {
|
||||||
|
moduleFd = fd.detachFd();
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.w(TAG, "someone chattr +i ?", e);
|
||||||
|
}
|
||||||
|
runLogcat(thread.getId(), moduleFd, verboseLog);
|
||||||
Log.i(TAG, "stoped");
|
Log.i(TAG, "stoped");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private native void runLogcat(long tid, int fd, boolean verboseLog);
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
private int refreshFd(boolean isVerboseLog) {
|
private int refreshFd() {
|
||||||
File log;
|
log = new File(logPath, logTimeFormat.format(Instant.now()) + ".log");
|
||||||
if (isVerboseLog) {
|
|
||||||
verboseLog = ConfigFileManager.getNewVerboseLogPath();
|
|
||||||
log = verboseLog;
|
|
||||||
} else {
|
|
||||||
modulesLog = ConfigFileManager.getNewModulesLogPath();
|
|
||||||
log = modulesLog;
|
|
||||||
}
|
|
||||||
try (var fd = ParcelFileDescriptor.open(log, mode)) {
|
try (var fd = ParcelFileDescriptor.open(log, mode)) {
|
||||||
return fd.detachFd();
|
return fd.detachFd();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
|
@ -52,11 +61,8 @@ public class LogcatService implements Runnable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isRunning() {
|
|
||||||
return thread != null && thread.isAlive();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void start() {
|
public void start() {
|
||||||
|
if (thread != null) Log.i(TAG, "!!start_verbose!!" + thread.getId());
|
||||||
if (isRunning()) return;
|
if (isRunning()) return;
|
||||||
thread = new Thread(this);
|
thread = new Thread(this);
|
||||||
thread.setName("logcat");
|
thread.setName("logcat");
|
||||||
|
|
@ -68,19 +74,21 @@ public class LogcatService implements Runnable {
|
||||||
thread.start();
|
thread.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startVerbose() {
|
public void stop() {
|
||||||
Log.i(TAG, "!!start_verbose!!" + thread.getId());
|
// logcat thread is listening for this keyword
|
||||||
}
|
|
||||||
|
|
||||||
public void stopVerbose() {
|
|
||||||
Log.i(TAG, "!!stop_verbose!!" + thread.getId());
|
Log.i(TAG, "!!stop_verbose!!" + thread.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
public boolean isRunning() {
|
||||||
public File getVerboseLog() {
|
return thread != null && thread.isAlive();
|
||||||
return verboseLog;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public File getLog() {
|
||||||
|
return log;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
public File getModulesLog() {
|
public File getModulesLog() {
|
||||||
return modulesLog;
|
return modulesLog;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,6 @@ public class ServiceManager {
|
||||||
public static final String TAG = "LSPosedService";
|
public static final String TAG = "LSPosedService";
|
||||||
private static final ConcurrentHashMap<String, LSPModuleService> moduleServices = new ConcurrentHashMap<>();
|
private static final ConcurrentHashMap<String, LSPModuleService> moduleServices = new ConcurrentHashMap<>();
|
||||||
private static final File globalNamespace = new File("/proc/1/root");
|
private static final File globalNamespace = new File("/proc/1/root");
|
||||||
@SuppressWarnings("FieldCanBeLocal")
|
|
||||||
private static LSPosedService mainService = null;
|
private static LSPosedService mainService = null;
|
||||||
private static LSPApplicationService applicationService = null;
|
private static LSPApplicationService applicationService = null;
|
||||||
private static LSPManagerService managerService = null;
|
private static LSPManagerService managerService = null;
|
||||||
|
|
@ -51,7 +50,6 @@ public class ServiceManager {
|
||||||
while (android.os.ServiceManager.getService(name) == null) {
|
while (android.os.ServiceManager.getService(name) == null) {
|
||||||
try {
|
try {
|
||||||
Log.i(TAG, "service " + name + " is not started, wait 1s.");
|
Log.i(TAG, "service " + name + " is not started, wait 1s.");
|
||||||
//noinspection BusyWait
|
|
||||||
Thread.sleep(1000);
|
Thread.sleep(1000);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
Log.i(TAG, Log.getStackTraceString(e));
|
Log.i(TAG, Log.getStackTraceString(e));
|
||||||
|
|
@ -65,7 +63,7 @@ public class ServiceManager {
|
||||||
|
|
||||||
// call by ourselves
|
// call by ourselves
|
||||||
public static void start(String[] args) {
|
public static void start(String[] args) {
|
||||||
if (!ConfigFileManager.tryLock()) System.exit(0);
|
if (!ConfigManager.getInstance().tryLock()) System.exit(0);
|
||||||
|
|
||||||
for (String arg : args) {
|
for (String arg : args) {
|
||||||
if (arg.equals("--from-service")) {
|
if (arg.equals("--from-service")) {
|
||||||
|
|
@ -80,11 +78,8 @@ public class ServiceManager {
|
||||||
System.exit(1);
|
System.exit(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
logcatService = new LogcatService();
|
logcatService = new LogcatService(ConfigManager.getLogPath());
|
||||||
logcatService.start();
|
logcatService.start();
|
||||||
if (ConfigManager.getInstance().verboseLog()) {
|
|
||||||
logcatService.startVerbose();
|
|
||||||
}
|
|
||||||
|
|
||||||
Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
|
Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
|
||||||
Looper.prepareMainLooper();
|
Looper.prepareMainLooper();
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,7 @@
|
||||||
package android.os;
|
package android.os;
|
||||||
|
|
||||||
public class SELinux {
|
public class SELinux {
|
||||||
public static boolean checkSELinuxAccess(String scon, String tcon, String tclass, String perm) {
|
public static final boolean checkSELinuxAccess(String scon, String tcon, String tclass, String perm){
|
||||||
throw new UnsupportedOperationException("Stub");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean setFileContext(String path, String context) {
|
|
||||||
throw new UnsupportedOperationException("Stub");
|
throw new UnsupportedOperationException("Stub");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue