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 killLspd = task("killLspd", Exec::class) {
|
||||
commandLine(adb, "shell", "su", "-c", "killall -w lspd")
|
||||
isIgnoreExitValue = true
|
||||
}
|
||||
val pushLspd = task("pushLspd", Exec::class) {
|
||||
dependsOn("mergeDexDebug")
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
#!/sbin/sh
|
||||
|
||||
#################
|
||||
# Initialization
|
||||
#################
|
||||
|
|
@ -21,9 +23,11 @@ require_new_magisk() {
|
|||
OUTFD=$2
|
||||
ZIPFILE=$3
|
||||
|
||||
mount /data 2>/dev/null
|
||||
|
||||
[ -f /data/adb/magisk/util_functions.sh ] || require_new_magisk
|
||||
. /data/adb/magisk/util_functions.sh
|
||||
[ $MAGISK_VER_CODE -lt 20400 ] && require_new_magisk
|
||||
|
||||
install_module
|
||||
exit 0
|
||||
exit 0
|
||||
|
|
@ -120,4 +120,26 @@ fi
|
|||
set_perm_recursive "$MODPATH" 0 0 0755 0644
|
||||
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!"
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
#!/system/bin/sh
|
||||
|
||||
#
|
||||
# This file is part of LSPosed.
|
||||
#
|
||||
|
|
@ -20,5 +22,22 @@
|
|||
|
||||
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"
|
||||
unshare -m sh -c "$MODDIR/lspd &"
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
#!/system/bin/sh
|
||||
|
||||
#
|
||||
# This file is part of LSPosed.
|
||||
#
|
||||
|
|
@ -19,4 +21,4 @@
|
|||
|
||||
MODDIR=${0%/*}
|
||||
# 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.
|
||||
#
|
||||
|
|
@ -19,3 +21,4 @@
|
|||
|
||||
rm -rf /data/adb/lspd
|
||||
rm -rf /data/adb/riru/modules/lspd
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
public static android.os.IBinder getApplicationServiceForSystemServer(android.os.IBinder, android.os.IBinder);
|
||||
}
|
||||
-keepclasseswithmembers class org.lsposed.lspd.service.LogcatService {
|
||||
private int refreshFd(boolean);
|
||||
private int refreshFd();
|
||||
}
|
||||
-assumenosideeffects class android.util.Log {
|
||||
public static *** v(...);
|
||||
|
|
|
|||
|
|
@ -27,20 +27,22 @@ public:
|
|||
|
||||
UniqueFile(int fd, const char *mode) : UniqueFile(fd > 0 ? fdopen(fd, mode) : stdout) {};
|
||||
|
||||
UniqueFile() : UniqueFile(stdout) {};
|
||||
UniqueFile() : UniqueFile(nullptr) {};
|
||||
};
|
||||
|
||||
class Logcat {
|
||||
public:
|
||||
explicit Logcat(JNIEnv *env, jobject thiz, jmethodID method, jlong logger_id) :
|
||||
env_(env), thiz_(thiz), refresh_fd_method_(method), logger_id_(logger_id),
|
||||
stop_verbose_inst_("!!stop_verbose!!" + std::to_string(logger_id_)),
|
||||
start_verbose_inst_("!!start_verbose!!" + std::to_string(logger_id_)) {}
|
||||
explicit Logcat(JNIEnv *env, jobject thiz, jmethodID method, jlong tid, jint fd,
|
||||
jboolean verbose) :
|
||||
env_(env), thiz_(thiz), refresh_fd_method_(method), tid_(tid), module_file_(fd, "w"),
|
||||
verbose_(verbose),
|
||||
stop_verbose_inst_("!!stop_verbose!!" + std::to_string(tid_)),
|
||||
start_verbose_inst_("!!start_verbose!!" + std::to_string(tid_)) {}
|
||||
|
||||
[[noreturn]] void Run();
|
||||
|
||||
private:
|
||||
inline void RefreshFd(bool is_verbose);
|
||||
inline void RefreshFd();
|
||||
|
||||
void ProcessBuffer(struct log_msg *buf);
|
||||
|
||||
|
|
@ -49,17 +51,15 @@ private:
|
|||
JNIEnv *env_;
|
||||
jobject thiz_;
|
||||
jmethodID refresh_fd_method_;
|
||||
jlong logger_id_;
|
||||
jlong tid_;
|
||||
UniqueFile module_file_{};
|
||||
size_t module_count_ = 0;
|
||||
|
||||
UniqueFile modules_file_{};
|
||||
size_t modules_file_part_ = 0;
|
||||
size_t modules_print_count_ = 0;
|
||||
UniqueFile out_file_{};
|
||||
size_t print_count_ = 0;
|
||||
size_t file_count_ = 1;
|
||||
|
||||
UniqueFile verbose_file_{};
|
||||
size_t verbose_file_part_ = 0;
|
||||
size_t verbose_print_count_ = 0;
|
||||
|
||||
bool verbose_ = false;
|
||||
bool verbose_ = true;
|
||||
|
||||
const std::string stop_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);
|
||||
}
|
||||
|
||||
void Logcat::RefreshFd(bool is_verbose) {
|
||||
constexpr const char *start_info = "----%" PRId64 "-%zu start----\n";
|
||||
constexpr const char *stop_info = "----%" PRId64 "-%zu end----\n";
|
||||
if (is_verbose) {
|
||||
verbose_print_count_ = 0;
|
||||
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::RefreshFd() {
|
||||
print_count_ = 0;
|
||||
out_file_ = UniqueFile(env_->CallIntMethod(thiz_, refresh_fd_method_), "w");
|
||||
fprintf(out_file_.get(), "%" PRId64 "-%zu\n", tid_, file_count_);
|
||||
file_count_++;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
std::string_view tag(entry.tag);
|
||||
bool shortcut = false;
|
||||
bool skip = false;
|
||||
if (tag == "LSPosed-Bridge" || tag == "XSharedPreferences") [[unlikely]] {
|
||||
modules_print_count_ += PrintLogLine(entry, modules_file_.get());
|
||||
shortcut = true;
|
||||
module_count_ += PrintLogLine(entry, module_file_.get());
|
||||
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.starts_with("Riru") ||
|
||||
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 (std::string_view(entry.message) == stop_verbose_inst_) {
|
||||
verbose_ = false;
|
||||
} else if (std::string_view(entry.message) == start_verbose_inst_) {
|
||||
RefreshFd(true);
|
||||
verbose_ = true;
|
||||
}
|
||||
if (std::string_view(entry.message) == stop_verbose_inst_) verbose_ = false;
|
||||
if (std::string_view(entry.message) == start_verbose_inst_) verbose_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Logcat::Run() {
|
||||
constexpr size_t tail_after_crash = 10U;
|
||||
size_t tail = 0;
|
||||
RefreshFd(false);
|
||||
|
||||
while (true) {
|
||||
std::unique_ptr<logger_list, decltype(&android_logger_list_free)> logger_list{
|
||||
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);
|
||||
}
|
||||
|
||||
RefreshFd();
|
||||
|
||||
struct log_msg msg{};
|
||||
|
||||
while (true) {
|
||||
|
|
@ -157,14 +144,18 @@ void Logcat::Run() {
|
|||
|
||||
ProcessBuffer(&msg);
|
||||
|
||||
fflush(verbose_file_.get());
|
||||
fflush(modules_file_.get());
|
||||
fflush(out_file_.get());
|
||||
fflush(module_file_.get());
|
||||
|
||||
if (verbose_print_count_ >= kMaxLogSize) [[unlikely]] RefreshFd(true);
|
||||
if (modules_print_count_ >= kMaxLogSize) [[unlikely]] RefreshFd(false);
|
||||
if (print_count_ >= kMaxLogSize) [[unlikely]] RefreshFd();
|
||||
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(modules_file_.get(), "\nLogd maybe crashed, retrying in 1s...\n");
|
||||
fprintf(out_file_.get(),
|
||||
"\nLogd maybe crashed, retrying in %" PRId64 "-%zu file after 1s\n",
|
||||
tid_, file_count_ + 1);
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
|
@ -172,9 +163,10 @@ void Logcat::Run() {
|
|||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
// 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);
|
||||
jmethodID method = env->GetMethodID(clazz, "refreshFd", "(Z)I");
|
||||
Logcat logcat(env, thiz, method, logger_id);
|
||||
jmethodID method = env->GetMethodID(clazz, "refreshFd", "()I");
|
||||
Logcat logcat(env, thiz, method, tid, fd, verbose);
|
||||
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.ServiceManager.TAG;
|
||||
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 android.content.ContentValues;
|
||||
|
|
@ -62,8 +63,12 @@ import java.io.InputStreamReader;
|
|||
import java.io.OutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.FileLock;
|
||||
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.Arrays;
|
||||
import java.util.Collections;
|
||||
|
|
@ -74,24 +79,65 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
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 {
|
||||
private static ConfigManager instance = null;
|
||||
|
||||
private final SQLiteDatabase db =
|
||||
SQLiteDatabase.openOrCreateDatabase(ConfigFileManager.dbPath, null);
|
||||
private static final File basePath = new File("/data/adb/lspd");
|
||||
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 static final File resourceHookSwitch = new File(configPath, "enable_resources");
|
||||
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 final String manager = BuildConfig.DEFAULT_MANAGER_PACKAGE_NAME;
|
||||
private int managerUid = -1;
|
||||
private static final File logPath = new File(basePath, "log");
|
||||
|
||||
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;
|
||||
|
||||
|
|
@ -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," +
|
||||
"module_pkg_name text NOT NULL UNIQUE," +
|
||||
"apk_path text NOT NULL, " +
|
||||
"enabled BOOLEAN DEFAULT 0 " +
|
||||
"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," +
|
||||
"app_pkg_name text NOT NULL," +
|
||||
"user_id integer NOT NULL," +
|
||||
"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," +
|
||||
"user_id integer 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
|
||||
public boolean shouldSkipSystemServer() {
|
||||
if (!SELinux.checkSELinuxAccess("u:r:system_server:s0", "u:r:system_server:s0", "process", "execmem")) {
|
||||
|
|
@ -207,40 +269,55 @@ public class ConfigManager {
|
|||
return modules;
|
||||
}
|
||||
|
||||
private synchronized void updateConfig() {
|
||||
ConfigFileManager.migrateOldConfig(this);
|
||||
Map<String, Object> config = getModulePrefs("lspd", 0, "config");
|
||||
private static String readText(@NonNull File file) throws IOException {
|
||||
return new String(Files.readAllBytes(file.toPath())).trim();
|
||||
}
|
||||
|
||||
Object bool = config.get("enable_resources");
|
||||
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;
|
||||
}
|
||||
private static String readText(@NonNull File file, String defaultValue) {
|
||||
try {
|
||||
Files.createDirectories(Paths.get(miscPath));
|
||||
SELinux.setFileContext(miscPath, "u:object_r:magisk_file:s0");
|
||||
if (!file.exists()) return defaultValue;
|
||||
return readText(file);
|
||||
} catch (IOException 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();
|
||||
}
|
||||
|
||||
public synchronized void updateManager() {
|
||||
if (!packageStarted) return;
|
||||
try {
|
||||
PackageInfo info = PackageService.getPackageInfo(manager, 0, 0);
|
||||
PackageInfo info = PackageService.getPackageInfo(readText(managerPath, BuildConfig.DEFAULT_MANAGER_PACKAGE_NAME), 0, 0);
|
||||
if (info != null) {
|
||||
managerUid = info.applicationInfo.uid;
|
||||
manager = info.packageName;
|
||||
} else {
|
||||
Log.w(TAG, "manager is not installed");
|
||||
}
|
||||
|
|
@ -251,12 +328,19 @@ public class ConfigManager {
|
|||
public void ensureManager() {
|
||||
if (!packageStarted) return;
|
||||
new Thread(() -> {
|
||||
if (PackageService.installManagerIfAbsent(manager, ConfigFileManager.managerApkPath)) {
|
||||
updateManager();
|
||||
if (PackageService.installManagerIfAbsent(manager, new File(basePath, "manager.apk"))) {
|
||||
updateManager(BuildConfig.DEFAULT_MANAGER_PACKAGE_NAME);
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
public synchronized void updateManager(@NonNull String packageName) {
|
||||
Log.i(TAG, "Now manager is " + packageName);
|
||||
writeText(managerPath, packageName);
|
||||
manager = packageName;
|
||||
updateManager();
|
||||
}
|
||||
|
||||
static ConfigManager getInstance() {
|
||||
if (instance == null)
|
||||
instance = new ConfigManager();
|
||||
|
|
@ -302,7 +386,7 @@ public class ConfigManager {
|
|||
Map<String, ConcurrentHashMap<String, Object>> fetchModuleConfig(String name, int user_id) {
|
||||
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)) {
|
||||
if (cursor == null) {
|
||||
Log.e(TAG, "db cache failed");
|
||||
|
|
@ -329,21 +413,19 @@ public class ConfigManager {
|
|||
if (value instanceof Serializable) {
|
||||
prefs.put(key, value);
|
||||
var values = new ContentValues();
|
||||
values.put("`group`", group);
|
||||
values.put("`key`", key);
|
||||
values.put("data", SerializationUtils.serialize((Serializable) value));
|
||||
values.put("module_pkg_name", moduleName);
|
||||
values.put("user_id", String.valueOf(userId));
|
||||
db.insertWithOnConflict("config", null, values, SQLiteDatabase.CONFLICT_REPLACE);
|
||||
values.put("group", group);
|
||||
values.put("key", key);
|
||||
values.put("value", SerializationUtils.serialize((Serializable) value));
|
||||
db.updateWithOnConflict("config", values, "module_pkg_name=? and user_id=?", new String[]{moduleName, String.valueOf(userId)}, SQLiteDatabase.CONFLICT_REPLACE);
|
||||
} else {
|
||||
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) {
|
||||
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() {
|
||||
|
|
@ -621,11 +703,9 @@ public class ConfigManager {
|
|||
Log.w(TAG, "update module apk path should not be called inside transaction");
|
||||
return false;
|
||||
}
|
||||
|
||||
ContentValues values = new ContentValues();
|
||||
values.put("module_pkg_name", packageName);
|
||||
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);
|
||||
if (count < 0) {
|
||||
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) {
|
||||
updateModulePrefs("lspd", 0, "config", "enable_resources", resourceHook);
|
||||
writeInt(resourceHookSwitch, resourceHook ? 1 : 0);
|
||||
this.resourceHook = resourceHook;
|
||||
}
|
||||
|
||||
public void setVerboseLog(boolean on) {
|
||||
var logcatService = ServiceManager.getLogcatService();
|
||||
if (on) {
|
||||
logcatService.startVerbose();
|
||||
logcatService.start();
|
||||
} else {
|
||||
logcatService.stopVerbose();
|
||||
logcatService.stop();
|
||||
}
|
||||
updateModulePrefs("lspd", 0, "config", "enable_verbose_log", on);
|
||||
verboseLog = on;
|
||||
logcat = on;
|
||||
}
|
||||
|
||||
public boolean resourceHook() {
|
||||
|
|
@ -802,14 +881,17 @@ public class ConfigManager {
|
|||
}
|
||||
|
||||
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 {
|
||||
var modulesLog = ServiceManager.getLogcatService().getModulesLog();
|
||||
if (modulesLog == null) return null;
|
||||
return ParcelFileDescriptor.open(modulesLog, ParcelFileDescriptor.MODE_READ_ONLY);
|
||||
return ParcelFileDescriptor.open(modulesLog, mode);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, Log.getStackTraceString(e));
|
||||
return null;
|
||||
|
|
@ -818,9 +900,9 @@ public class ConfigManager {
|
|||
|
||||
public ParcelFileDescriptor getVerboseLog() {
|
||||
try {
|
||||
var verboseLog = ServiceManager.getLogcatService().getVerboseLog();
|
||||
if (verboseLog == null) return null;
|
||||
return ParcelFileDescriptor.open(verboseLog, ParcelFileDescriptor.MODE_READ_ONLY);
|
||||
var logcat = ServiceManager.getLogcatService().getLog();
|
||||
if (logcat == null) return null;
|
||||
return ParcelFileDescriptor.open(logcat, ParcelFileDescriptor.MODE_READ_ONLY);
|
||||
} catch (FileNotFoundException e) {
|
||||
Log.e(TAG, Log.getStackTraceString(e));
|
||||
return null;
|
||||
|
|
@ -828,11 +910,11 @@ public class ConfigManager {
|
|||
}
|
||||
|
||||
public boolean clearLogs(boolean verbose) {
|
||||
var logcatService = ServiceManager.getLogcatService();
|
||||
File logFile = verbose ? logcatService.getVerboseLog() : logcatService.getModulesLog();
|
||||
if (logFile == null) return true;
|
||||
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();
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
|
|
@ -842,7 +924,7 @@ public class ConfigManager {
|
|||
}
|
||||
|
||||
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) {
|
||||
|
|
@ -855,7 +937,7 @@ public class ConfigManager {
|
|||
|
||||
public String getPrefsPath(String fileName, int uid) {
|
||||
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
|
||||
|
|
|
|||
|
|
@ -176,7 +176,7 @@ public class LSPManagerService extends ILSPManagerService.Stub {
|
|||
|
||||
@Override
|
||||
public ParcelFileDescriptor getModulesLog() {
|
||||
return ConfigManager.getInstance().getModulesLog();
|
||||
return ConfigManager.getInstance().getModulesLog(ParcelFileDescriptor.MODE_READ_ONLY);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -4,46 +4,55 @@ import android.annotation.SuppressLint;
|
|||
import android.os.ParcelFileDescriptor;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.lsposed.lspd.util.Utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
public class LogcatService implements Runnable {
|
||||
private static final String TAG = "LSPosedLogcat";
|
||||
private static final int mode = ParcelFileDescriptor.MODE_WRITE_ONLY |
|
||||
ParcelFileDescriptor.MODE_CREATE |
|
||||
ParcelFileDescriptor.MODE_TRUNCATE |
|
||||
ParcelFileDescriptor.MODE_APPEND;
|
||||
private File modulesLog = null;
|
||||
private File verboseLog = null;
|
||||
private static final int mode = ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_CREATE |
|
||||
ParcelFileDescriptor.MODE_TRUNCATE | ParcelFileDescriptor.MODE_APPEND;
|
||||
public final File modulesLog;
|
||||
private final File logPath;
|
||||
private final DateTimeFormatter logTimeFormat;
|
||||
private File log = null;
|
||||
private Thread thread = null;
|
||||
boolean verboseLog = true;
|
||||
|
||||
@SuppressLint("UnsafeDynamicallyLoadedCode")
|
||||
public LogcatService() {
|
||||
public LogcatService(File logPath) {
|
||||
String libraryPath = System.getProperty("lsp.library.path");
|
||||
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
|
||||
public void run() {
|
||||
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");
|
||||
}
|
||||
|
||||
private native void runLogcat(long tid, int fd, boolean verboseLog);
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private int refreshFd(boolean isVerboseLog) {
|
||||
File log;
|
||||
if (isVerboseLog) {
|
||||
verboseLog = ConfigFileManager.getNewVerboseLogPath();
|
||||
log = verboseLog;
|
||||
} else {
|
||||
modulesLog = ConfigFileManager.getNewModulesLogPath();
|
||||
log = modulesLog;
|
||||
}
|
||||
private int refreshFd() {
|
||||
log = new File(logPath, logTimeFormat.format(Instant.now()) + ".log");
|
||||
|
||||
try (var fd = ParcelFileDescriptor.open(log, mode)) {
|
||||
return fd.detachFd();
|
||||
} catch (IOException e) {
|
||||
|
|
@ -52,11 +61,8 @@ public class LogcatService implements Runnable {
|
|||
}
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return thread != null && thread.isAlive();
|
||||
}
|
||||
|
||||
public void start() {
|
||||
if (thread != null) Log.i(TAG, "!!start_verbose!!" + thread.getId());
|
||||
if (isRunning()) return;
|
||||
thread = new Thread(this);
|
||||
thread.setName("logcat");
|
||||
|
|
@ -68,19 +74,21 @@ public class LogcatService implements Runnable {
|
|||
thread.start();
|
||||
}
|
||||
|
||||
public void startVerbose() {
|
||||
Log.i(TAG, "!!start_verbose!!" + thread.getId());
|
||||
}
|
||||
|
||||
public void stopVerbose() {
|
||||
public void stop() {
|
||||
// logcat thread is listening for this keyword
|
||||
Log.i(TAG, "!!stop_verbose!!" + thread.getId());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public File getVerboseLog() {
|
||||
return verboseLog;
|
||||
public boolean isRunning() {
|
||||
return thread != null && thread.isAlive();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public File getLog() {
|
||||
return log;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public File getModulesLog() {
|
||||
return modulesLog;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@ public class ServiceManager {
|
|||
public static final String TAG = "LSPosedService";
|
||||
private static final ConcurrentHashMap<String, LSPModuleService> moduleServices = new ConcurrentHashMap<>();
|
||||
private static final File globalNamespace = new File("/proc/1/root");
|
||||
@SuppressWarnings("FieldCanBeLocal")
|
||||
private static LSPosedService mainService = null;
|
||||
private static LSPApplicationService applicationService = null;
|
||||
private static LSPManagerService managerService = null;
|
||||
|
|
@ -51,7 +50,6 @@ public class ServiceManager {
|
|||
while (android.os.ServiceManager.getService(name) == null) {
|
||||
try {
|
||||
Log.i(TAG, "service " + name + " is not started, wait 1s.");
|
||||
//noinspection BusyWait
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
Log.i(TAG, Log.getStackTraceString(e));
|
||||
|
|
@ -65,7 +63,7 @@ public class ServiceManager {
|
|||
|
||||
// call by ourselves
|
||||
public static void start(String[] args) {
|
||||
if (!ConfigFileManager.tryLock()) System.exit(0);
|
||||
if (!ConfigManager.getInstance().tryLock()) System.exit(0);
|
||||
|
||||
for (String arg : args) {
|
||||
if (arg.equals("--from-service")) {
|
||||
|
|
@ -80,11 +78,8 @@ public class ServiceManager {
|
|||
System.exit(1);
|
||||
});
|
||||
|
||||
logcatService = new LogcatService();
|
||||
logcatService = new LogcatService(ConfigManager.getLogPath());
|
||||
logcatService.start();
|
||||
if (ConfigManager.getInstance().verboseLog()) {
|
||||
logcatService.startVerbose();
|
||||
}
|
||||
|
||||
Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
|
||||
Looper.prepareMainLooper();
|
||||
|
|
|
|||
|
|
@ -1,11 +1,7 @@
|
|||
package android.os;
|
||||
|
||||
public class SELinux {
|
||||
public static boolean checkSELinuxAccess(String scon, String tcon, String tclass, String perm) {
|
||||
throw new UnsupportedOperationException("Stub");
|
||||
}
|
||||
|
||||
public static boolean setFileContext(String path, String context) {
|
||||
public static final boolean checkSELinuxAccess(String scon, String tcon, String tclass, String perm){
|
||||
throw new UnsupportedOperationException("Stub");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue