diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 50e1e138..c90a1f29 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -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") 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 26911f4b..ea4889e7 100644 --- a/core/magisk_module/META-INF/com/google/android/update-binary +++ b/core/magisk_module/META-INF/com/google/android/update-binary @@ -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 \ No newline at end of file diff --git a/core/magisk_module/customize.sh b/core/magisk_module/customize.sh index 4228ac30..9c3457e5 100644 --- a/core/magisk_module/customize.sh +++ b/core/magisk_module/customize.sh @@ -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' /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 0776e819..03c8f02e 100644 --- a/core/magisk_module/post-fs-data.sh +++ b/core/magisk_module/post-fs-data.sh @@ -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 &" diff --git a/core/magisk_module/service.sh b/core/magisk_module/service.sh index 8d71c3c6..eb0b6c9f 100644 --- a/core/magisk_module/service.sh +++ b/core/magisk_module/service.sh @@ -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 & diff --git a/core/magisk_module/uninstall.sh b/core/magisk_module/uninstall.sh index fc913d83..51abfe11 100644 --- a/core/magisk_module/uninstall.sh +++ b/core/magisk_module/uninstall.sh @@ -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 + diff --git a/core/proguard-rules.pro b/core/proguard-rules.pro index e3012624..5cedf3e5 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(boolean); + private int refreshFd(); } -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 b7e6a3da..e5da37c9 100644 --- a/core/src/main/cpp/daemon/logcat.cpp +++ b/core/src/main/cpp/daemon/logcat.cpp @@ -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(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{ 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(); } diff --git a/core/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java b/core/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java deleted file mode 100644 index d6a0555e..00000000 --- a/core/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java +++ /dev/null @@ -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(); - 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 3e284d90..83e95a6a 100644 --- a/core/src/main/java/org/lsposed/lspd/service/ConfigManager.java +++ b/core/src/main/java/org/lsposed/lspd/service/ConfigManager.java @@ -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(); + 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 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> 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"); @@ -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 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 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 5c6a0065..34f3ef14 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(); + return ConfigManager.getInstance().getModulesLog(ParcelFileDescriptor.MODE_READ_ONLY); } @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 eb458ae6..7c3d684e 100644 --- a/core/src/main/java/org/lsposed/lspd/service/LogcatService.java +++ b/core/src/main/java/org/lsposed/lspd/service/LogcatService.java @@ -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; } 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 2466bdad..055b53fd 100644 --- a/core/src/main/java/org/lsposed/lspd/service/ServiceManager.java +++ b/core/src/main/java/org/lsposed/lspd/service/ServiceManager.java @@ -40,7 +40,6 @@ 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; @@ -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(); diff --git a/hiddenapi-stubs/src/main/java/android/os/SELinux.java b/hiddenapi-stubs/src/main/java/android/os/SELinux.java index 4dfe1de7..78786849 100644 --- a/hiddenapi-stubs/src/main/java/android/os/SELinux.java +++ b/hiddenapi-stubs/src/main/java/android/os/SELinux.java @@ -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"); } }