Revert "[core] Move config to db (#981)" (#988)

This reverts commit 40e47762d7.
This commit is contained in:
vvb2060 2021-08-23 22:28:41 +08:00 committed by GitHub
parent 394d7fba19
commit 485da5a37c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 284 additions and 314 deletions

View File

@ -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")

View File

@ -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

View File

@ -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!"

View File

@ -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 &"

View File

@ -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 &

View File

@ -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

View File

@ -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(...);

View File

@ -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();
} }

View File

@ -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();
}
}
}

View File

@ -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

View File

@ -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

View File

@ -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;
} }

View File

@ -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();

View File

@ -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");
} }
} }