diff --git a/core/build.gradle.kts b/core/build.gradle.kts index c90a1f29..50e1e138 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -246,6 +246,7 @@ 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") diff --git a/core/magisk_module/META-INF/com/google/android/update-binary b/core/magisk_module/META-INF/com/google/android/update-binary index ea4889e7..26911f4b 100644 --- a/core/magisk_module/META-INF/com/google/android/update-binary +++ b/core/magisk_module/META-INF/com/google/android/update-binary @@ -1,5 +1,3 @@ -#!/sbin/sh - ################# # Initialization ################# @@ -23,11 +21,9 @@ 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 \ No newline at end of file +exit 0 diff --git a/core/magisk_module/customize.sh b/core/magisk_module/customize.sh index 9c3457e5..4228ac30 100644 --- a/core/magisk_module/customize.sh +++ b/core/magisk_module/customize.sh @@ -120,26 +120,4 @@ 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' /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!" diff --git a/core/magisk_module/post-fs-data.sh b/core/magisk_module/post-fs-data.sh index 03c8f02e..0776e819 100644 --- a/core/magisk_module/post-fs-data.sh +++ b/core/magisk_module/post-fs-data.sh @@ -1,5 +1,3 @@ -#!/system/bin/sh - # # This file is part of LSPosed. # @@ -22,22 +20,5 @@ 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 &" diff --git a/core/magisk_module/service.sh b/core/magisk_module/service.sh index eb0b6c9f..8d71c3c6 100644 --- a/core/magisk_module/service.sh +++ b/core/magisk_module/service.sh @@ -1,5 +1,3 @@ -#!/system/bin/sh - # # This file is part of LSPosed. # @@ -21,4 +19,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 & diff --git a/core/magisk_module/uninstall.sh b/core/magisk_module/uninstall.sh index 51abfe11..fc913d83 100644 --- a/core/magisk_module/uninstall.sh +++ b/core/magisk_module/uninstall.sh @@ -1,5 +1,3 @@ -#!/sbin/sh - # # This file is part of LSPosed. # @@ -21,4 +19,3 @@ rm -rf /data/adb/lspd rm -rf /data/adb/riru/modules/lspd - diff --git a/core/proguard-rules.pro b/core/proguard-rules.pro index 5cedf3e5..e3012624 100644 --- a/core/proguard-rules.pro +++ b/core/proguard-rules.pro @@ -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(); + private int refreshFd(boolean); } -assumenosideeffects class android.util.Log { public static *** v(...); diff --git a/core/src/main/cpp/daemon/logcat.cpp b/core/src/main/cpp/daemon/logcat.cpp index e5da37c9..b7e6a3da 100644 --- a/core/src/main/cpp/daemon/logcat.cpp +++ b/core/src/main/cpp/daemon/logcat.cpp @@ -27,22 +27,20 @@ public: UniqueFile(int fd, const char *mode) : UniqueFile(fd > 0 ? fdopen(fd, mode) : stdout) {}; - UniqueFile() : UniqueFile(nullptr) {}; + UniqueFile() : UniqueFile(stdout) {}; }; class Logcat { public: - 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_)) {} + 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_)) {} [[noreturn]] void Run(); private: - inline void RefreshFd(); + inline void RefreshFd(bool is_verbose); void ProcessBuffer(struct log_msg *buf); @@ -51,15 +49,17 @@ private: JNIEnv *env_; jobject thiz_; jmethodID refresh_fd_method_; - jlong tid_; - UniqueFile module_file_{}; - size_t module_count_ = 0; + jlong logger_id_; - UniqueFile out_file_{}; - size_t print_count_ = 0; - size_t file_count_ = 1; + UniqueFile modules_file_{}; + size_t modules_file_part_ = 0; + size_t modules_print_count_ = 0; - bool verbose_ = true; + UniqueFile verbose_file_{}; + size_t verbose_file_part_ = 0; + size_t verbose_print_count_ = 0; + + bool verbose_ = false; const std::string stop_verbose_inst_; const std::string start_verbose_inst_; @@ -91,11 +91,22 @@ int Logcat::PrintLogLine(const AndroidLogEntry &entry, FILE *out) { entry.tag, static_cast(message_len), message); } -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::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::ProcessBuffer(struct log_msg *buf) { @@ -103,27 +114,31 @@ void Logcat::ProcessBuffer(struct log_msg *buf) { if (android_log_processLogBuffer(&buf->entry, &entry) < 0) return; std::string_view tag(entry.tag); - bool skip = false; + bool shortcut = false; if (tag == "LSPosed-Bridge" || tag == "XSharedPreferences") [[unlikely]] { - module_count_ += PrintLogLine(entry, module_file_.get()); - skip = true; + modules_print_count_ += PrintLogLine(entry, modules_file_.get()); + shortcut = true; } - if (verbose_ && (skip || buf->id() == log_id::LOG_ID_CRASH || + if (verbose_ && (shortcut || buf->id() == log_id::LOG_ID_CRASH || tag == "Magisk" || tag.starts_with("Riru") || tag.starts_with("LSPosed"))) [[unlikely]] { - print_count_ += PrintLogLine(entry, out_file_.get()); + verbose_print_count_ += PrintLogLine(entry, verbose_file_.get()); } if (entry.pid == getpid() && tag == "LSPosedLogcat") [[unlikely]] { - if (std::string_view(entry.message) == stop_verbose_inst_) verbose_ = false; - if (std::string_view(entry.message) == start_verbose_inst_) verbose_ = true; + 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; + } } } void Logcat::Run() { constexpr size_t tail_after_crash = 10U; size_t tail = 0; - + RefreshFd(false); while (true) { std::unique_ptr logger_list{ android_logger_list_alloc(0, tail, 0), &android_logger_list_free}; @@ -135,8 +150,6 @@ void Logcat::Run() { android_logger_set_log_size(logger, kMaxLogSize); } - RefreshFd(); - struct log_msg msg{}; while (true) { @@ -144,18 +157,14 @@ void Logcat::Run() { ProcessBuffer(&msg); - fflush(out_file_.get()); - fflush(module_file_.get()); + fflush(verbose_file_.get()); + fflush(modules_file_.get()); - if (print_count_ >= kMaxLogSize) [[unlikely]] RefreshFd(); - if (module_count_ >= kMaxLogSize) [[unlikely]] { - ftruncate(fileno(module_file_.get()), 0); - module_count_ = 0; - } + if (verbose_print_count_ >= kMaxLogSize) [[unlikely]] RefreshFd(true); + if (modules_print_count_ >= kMaxLogSize) [[unlikely]] RefreshFd(false); } - fprintf(out_file_.get(), - "\nLogd maybe crashed, retrying in %" PRId64 "-%zu file after 1s\n", - tid_, file_count_ + 1); + fprintf(verbose_file_.get(), "\nLogd maybe crashed, retrying in 1s...\n"); + fprintf(modules_file_.get(), "\nLogd maybe crashed, retrying in 1s...\n"); sleep(1); } } @@ -163,10 +172,9 @@ void Logcat::Run() { extern "C" JNIEXPORT void JNICALL // NOLINTNEXTLINE -Java_org_lsposed_lspd_service_LogcatService_runLogcat(JNIEnv *env, jobject thiz, jlong tid, - jint fd, jboolean verbose) { +Java_org_lsposed_lspd_service_LogcatService_runLogcat(JNIEnv *env, jobject thiz, jlong logger_id) { jclass clazz = env->GetObjectClass(thiz); - jmethodID method = env->GetMethodID(clazz, "refreshFd", "()I"); - Logcat logcat(env, thiz, method, tid, fd, verbose); + jmethodID method = env->GetMethodID(clazz, "refreshFd", "(Z)I"); + Logcat logcat(env, thiz, method, logger_id); logcat.Run(); } diff --git a/core/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java b/core/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java new file mode 100644 index 00000000..d6a0555e --- /dev/null +++ b/core/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java @@ -0,0 +1,152 @@ +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(); + 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(); + } + } +} diff --git a/core/src/main/java/org/lsposed/lspd/service/ConfigManager.java b/core/src/main/java/org/lsposed/lspd/service/ConfigManager.java index 83e95a6a..3e284d90 100644 --- a/core/src/main/java/org/lsposed/lspd/service/ConfigManager.java +++ b/core/src/main/java/org/lsposed/lspd/service/ConfigManager.java @@ -23,7 +23,6 @@ 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; @@ -63,12 +62,8 @@ 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.OpenOption; -import java.nio.file.StandardOpenOption; -import java.nio.file.attribute.PosixFilePermissions; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -79,65 +74,24 @@ 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 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 final SQLiteDatabase db = + SQLiteDatabase.openOrCreateDatabase(ConfigFileManager.dbPath, null); private boolean packageStarted = false; - private static final File resourceHookSwitch = new File(configPath, "enable_resources"); private boolean resourceHook = false; - - 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 boolean verboseLog = true; private String miscPath = null; - 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 String manager = BuildConfig.DEFAULT_MANAGER_PACKAGE_NAME; + private int managerUid = -1; private final Handler cacheHandler; @@ -173,20 +127,20 @@ public class ConfigManager { } } - private static final SQLiteStatement createModulesTable = db.compileStatement("CREATE TABLE IF NOT EXISTS modules (" + + private 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 static final SQLiteStatement createScopeTable = db.compileStatement("CREATE TABLE IF NOT EXISTS scope (" + + private 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 static final SQLiteStatement createConfigTable = db.compileStatement("CREATE TABLE IF NOT EXISTS config (" + + private 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," + @@ -214,22 +168,6 @@ public class ConfigManager { } } - public boolean tryLock() { - var openOptions = new HashSet(); - 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")) { @@ -269,55 +207,40 @@ public class ConfigManager { return modules; } - private static String readText(@NonNull File file) throws IOException { - return new String(Files.readAllBytes(file.toPath())).trim(); - } - - private static String readText(@NonNull File file, String defaultValue) { - try { - 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"); + ConfigFileManager.migrateOldConfig(this); + Map config = getModulePrefs("lspd", 0, "config"); + + 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; + } + try { + Files.createDirectories(Paths.get(miscPath)); + SELinux.setFileContext(miscPath, "u:object_r:magisk_file:s0"); + } catch (IOException e) { + Log.e(TAG, Log.getStackTraceString(e)); + } + updateManager(); } public synchronized void updateManager() { if (!packageStarted) return; try { - PackageInfo info = PackageService.getPackageInfo(readText(managerPath, BuildConfig.DEFAULT_MANAGER_PACKAGE_NAME), 0, 0); + PackageInfo info = PackageService.getPackageInfo(manager, 0, 0); if (info != null) { managerUid = info.applicationInfo.uid; - manager = info.packageName; } else { Log.w(TAG, "manager is not installed"); } @@ -328,19 +251,12 @@ public class ConfigManager { public void ensureManager() { if (!packageStarted) return; new Thread(() -> { - if (PackageService.installManagerIfAbsent(manager, new File(basePath, "manager.apk"))) { - updateManager(BuildConfig.DEFAULT_MANAGER_PACKAGE_NAME); + if (PackageService.installManagerIfAbsent(manager, ConfigFileManager.managerApkPath)) { + updateManager(); } }).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(); @@ -386,7 +302,7 @@ public class ConfigManager { Map> fetchModuleConfig(String name, int user_id) { var config = new ConcurrentHashMap>(); - 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"); @@ -413,19 +329,21 @@ 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("value", SerializationUtils.serialize((Serializable) value)); - db.updateWithOnConflict("config", values, "module_pkg_name=? and user_id=?", new String[]{moduleName, String.valueOf(userId)}, SQLiteDatabase.CONFLICT_REPLACE); + 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); } else { prefs.remove(key); - db.delete("config", "module_pkg_name=? and user_id=?", new String[]{moduleName, String.valueOf(userId)}); + db.delete("config", "module_pkg_name=? and user_id=? and `group`=? and `key`=?", new String[]{moduleName, String.valueOf(userId), group, key}); } } public ConcurrentHashMap 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, null); + return config.getOrDefault(group, new ConcurrentHashMap<>()); } private synchronized void cacheModules() { @@ -703,9 +621,11 @@ 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); @@ -862,18 +782,19 @@ public class ConfigManager { } public void setResourceHook(boolean resourceHook) { - writeInt(resourceHookSwitch, resourceHook ? 1 : 0); + updateModulePrefs("lspd", 0, "config", "enable_resources", resourceHook); this.resourceHook = resourceHook; } public void setVerboseLog(boolean on) { var logcatService = ServiceManager.getLogcatService(); if (on) { - logcatService.start(); + logcatService.startVerbose(); } else { - logcatService.stop(); + logcatService.stopVerbose(); } - logcat = on; + updateModulePrefs("lspd", 0, "config", "enable_verbose_log", on); + verboseLog = on; } public boolean resourceHook() { @@ -881,17 +802,14 @@ public class ConfigManager { } public boolean verboseLog() { - return logcat; + return verboseLog; } - public static File getLogPath() { - return logPath; - } - - public ParcelFileDescriptor getModulesLog(int mode) { - var modulesLog = getLogcatService().getModulesLog(); + public ParcelFileDescriptor getModulesLog() { try { - return ParcelFileDescriptor.open(modulesLog, mode); + var modulesLog = ServiceManager.getLogcatService().getModulesLog(); + if (modulesLog == null) return null; + return ParcelFileDescriptor.open(modulesLog, ParcelFileDescriptor.MODE_READ_ONLY); } catch (IOException e) { Log.e(TAG, Log.getStackTraceString(e)); return null; @@ -900,9 +818,9 @@ public class ConfigManager { public ParcelFileDescriptor getVerboseLog() { try { - var logcat = ServiceManager.getLogcatService().getLog(); - if (logcat == null) return null; - return ParcelFileDescriptor.open(logcat, ParcelFileDescriptor.MODE_READ_ONLY); + var verboseLog = ServiceManager.getLogcatService().getVerboseLog(); + if (verboseLog == null) return null; + return ParcelFileDescriptor.open(verboseLog, ParcelFileDescriptor.MODE_READ_ONLY); } catch (FileNotFoundException e) { Log.e(TAG, Log.getStackTraceString(e)); return null; @@ -910,11 +828,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 { - var logcat = ServiceManager.getLogcatService().getLog(); - var moduleLog = ServiceManager.getLogcatService().getModulesLog(); - if (verbose && logcat == null) return true; - OutputStream os = new FileOutputStream(verbose ? logcat : moduleLog); + OutputStream os = new FileOutputStream(logFile); os.close(); return true; } catch (IOException e) { @@ -924,7 +842,7 @@ public class ConfigManager { } public boolean isManager(String packageName) { - return packageName.equals(manager) || packageName.equals(BuildConfig.DEFAULT_MANAGER_PACKAGE_NAME); + return packageName.equals(manager); } public boolean isManager(int uid) { @@ -937,7 +855,7 @@ public class ConfigManager { public String getPrefsPath(String fileName, int uid) { int userId = uid / PER_USER_RANGE; - return miscPath + File.separator + "prefs" + (userId == 0 ? "" : String.valueOf(userId)) + File.separator + fileName; + return miscPath + "/prefs" + (userId == 0 ? "" : String.valueOf(userId)) + "/" + fileName; } // this is slow, avoid using it diff --git a/core/src/main/java/org/lsposed/lspd/service/LSPManagerService.java b/core/src/main/java/org/lsposed/lspd/service/LSPManagerService.java index 34f3ef14..5c6a0065 100644 --- a/core/src/main/java/org/lsposed/lspd/service/LSPManagerService.java +++ b/core/src/main/java/org/lsposed/lspd/service/LSPManagerService.java @@ -176,7 +176,7 @@ public class LSPManagerService extends ILSPManagerService.Stub { @Override public ParcelFileDescriptor getModulesLog() { - return ConfigManager.getInstance().getModulesLog(ParcelFileDescriptor.MODE_READ_ONLY); + return ConfigManager.getInstance().getModulesLog(); } @Override diff --git a/core/src/main/java/org/lsposed/lspd/service/LogcatService.java b/core/src/main/java/org/lsposed/lspd/service/LogcatService.java index 7c3d684e..eb458ae6 100644 --- a/core/src/main/java/org/lsposed/lspd/service/LogcatService.java +++ b/core/src/main/java/org/lsposed/lspd/service/LogcatService.java @@ -4,55 +4,46 @@ 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; - public final File modulesLog; - private final File logPath; - private final DateTimeFormatter logTimeFormat; - private File log = null; + 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 Thread thread = null; - boolean verboseLog = true; @SuppressLint("UnsafeDynamicallyLoadedCode") - public LogcatService(File logPath) { + public LogcatService() { 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"); - 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); + runLogcat(thread.getId()); Log.i(TAG, "stoped"); } - private native void runLogcat(long tid, int fd, boolean verboseLog); - @SuppressWarnings("unused") - private int refreshFd() { - log = new File(logPath, logTimeFormat.format(Instant.now()) + ".log"); - + private int refreshFd(boolean isVerboseLog) { + File log; + if (isVerboseLog) { + verboseLog = ConfigFileManager.getNewVerboseLogPath(); + log = verboseLog; + } else { + modulesLog = ConfigFileManager.getNewModulesLogPath(); + log = modulesLog; + } try (var fd = ParcelFileDescriptor.open(log, mode)) { return fd.detachFd(); } catch (IOException e) { @@ -61,8 +52,11 @@ 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"); @@ -74,21 +68,19 @@ public class LogcatService implements Runnable { thread.start(); } - public void stop() { - // logcat thread is listening for this keyword + public void startVerbose() { + Log.i(TAG, "!!start_verbose!!" + thread.getId()); + } + + public void stopVerbose() { Log.i(TAG, "!!stop_verbose!!" + thread.getId()); } - public boolean isRunning() { - return thread != null && thread.isAlive(); - } - @Nullable - public File getLog() { - return log; + public File getVerboseLog() { + return verboseLog; } - @NonNull public File getModulesLog() { return modulesLog; } diff --git a/core/src/main/java/org/lsposed/lspd/service/ServiceManager.java b/core/src/main/java/org/lsposed/lspd/service/ServiceManager.java index 055b53fd..2466bdad 100644 --- a/core/src/main/java/org/lsposed/lspd/service/ServiceManager.java +++ b/core/src/main/java/org/lsposed/lspd/service/ServiceManager.java @@ -40,6 +40,7 @@ public class ServiceManager { public static final String TAG = "LSPosedService"; private static final ConcurrentHashMap 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; @@ -50,6 +51,7 @@ 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)); @@ -63,7 +65,7 @@ public class ServiceManager { // call by ourselves public static void start(String[] args) { - if (!ConfigManager.getInstance().tryLock()) System.exit(0); + if (!ConfigFileManager.tryLock()) System.exit(0); for (String arg : args) { if (arg.equals("--from-service")) { @@ -78,8 +80,11 @@ public class ServiceManager { System.exit(1); }); - logcatService = new LogcatService(ConfigManager.getLogPath()); + logcatService = new LogcatService(); logcatService.start(); + if (ConfigManager.getInstance().verboseLog()) { + logcatService.startVerbose(); + } Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND); Looper.prepareMainLooper(); diff --git a/hiddenapi-stubs/src/main/java/android/os/SELinux.java b/hiddenapi-stubs/src/main/java/android/os/SELinux.java index 78786849..4dfe1de7 100644 --- a/hiddenapi-stubs/src/main/java/android/os/SELinux.java +++ b/hiddenapi-stubs/src/main/java/android/os/SELinux.java @@ -1,7 +1,11 @@ package android.os; public class SELinux { - public static final boolean checkSELinuxAccess(String scon, String tcon, String tclass, String perm){ + public static 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"); } }