From 9f868aeddfc4093302964148df17fa7128b96625 Mon Sep 17 00:00:00 2001 From: vvb2060 Date: Sat, 21 Aug 2021 03:15:13 +0800 Subject: [PATCH] [core] Native logcat (#960) Co-authored-by: LoveSy --- .../manager/ui/fragment/SettingsFragment.java | 20 +-- core/build.gradle.kts | 6 + core/magisk_module/customize.sh | 16 +- core/magisk_module/lspd | 3 + core/magisk_module/post-fs-data.sh | 96 ----------- core/proguard-rules.pro | 3 + core/src/main/cpp/Android.mk | 1 + core/src/main/cpp/daemon/Android.mk | 9 + core/src/main/cpp/daemon/logcat.cpp | 155 ++++++++++++++++++ core/src/main/cpp/daemon/logcat.h | 62 +++++++ .../main/java/org/lsposed/lspd/core/Main.java | 2 - .../lsposed/lspd/service/ConfigManager.java | 34 ++-- .../lsposed/lspd/service/LogcatService.java | 75 +++++++++ .../lsposed/lspd/service/ServiceManager.java | 14 +- .../org/lsposed/lspd/util/ModuleLogger.java | 28 ++-- 15 files changed, 372 insertions(+), 152 deletions(-) create mode 100644 core/src/main/cpp/daemon/Android.mk create mode 100644 core/src/main/cpp/daemon/logcat.cpp create mode 100644 core/src/main/cpp/daemon/logcat.h create mode 100644 core/src/main/java/org/lsposed/lspd/service/LogcatService.java diff --git a/app/src/main/java/org/lsposed/manager/ui/fragment/SettingsFragment.java b/app/src/main/java/org/lsposed/manager/ui/fragment/SettingsFragment.java index 95d52272..ce5cb15d 100644 --- a/app/src/main/java/org/lsposed/manager/ui/fragment/SettingsFragment.java +++ b/app/src/main/java/org/lsposed/manager/ui/fragment/SettingsFragment.java @@ -144,22 +144,10 @@ public class SettingsFragment extends BaseFragment { boolean installed = ConfigManager.isBinderAlive(); SwitchPreference prefVerboseLogs = findPreference("disable_verbose_log"); if (prefVerboseLogs != null) { - if (requireActivity().getApplicationInfo().uid / 100000 != 0) { - prefVerboseLogs.setVisible(false); - } else { - prefVerboseLogs.setEnabled(installed); - prefVerboseLogs.setChecked(!installed || !ConfigManager.isVerboseLogEnabled()); - prefVerboseLogs.setOnPreferenceChangeListener((preference, newValue) -> { - boolean result = ConfigManager.setVerboseLogEnabled(!(boolean) newValue); - SettingsFragment fragment = (SettingsFragment) getParentFragment(); - if (result && fragment != null) { - Snackbar.make(fragment.binding.snackbar, R.string.reboot_required, Snackbar.LENGTH_SHORT) - .setAction(R.string.reboot, v -> ConfigManager.reboot(false)) - .show(); - } - return result; - }); - } + prefVerboseLogs.setEnabled(installed); + prefVerboseLogs.setChecked(!installed || !ConfigManager.isVerboseLogEnabled()); + prefVerboseLogs.setOnPreferenceChangeListener((preference, newValue) -> + ConfigManager.setVerboseLogEnabled(!(boolean) newValue)); } SwitchPreference prefEnableResources = findPreference("enable_resources"); diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 08d0d306..0eb93a13 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -252,8 +252,14 @@ val pushLspd = task("pushLspd", Exec::class) { workingDir("$buildDir/intermediates/dex/debug/mergeDexDebug") commandLine(adb, "push", "classes.dex", "/data/local/tmp/lspd.dex") } +val pushLspdNative = task("pushLspdNative", Exec::class) { + dependsOn("mergeDebugNativeLibs") + workingDir("$buildDir/intermediates/merged_native_libs/debug/out/lib/arm64-v8a") + commandLine(adb, "push", "libdaemon.so", "/data/local/tmp/libdaemon.so") +} task("reRunLspd", Exec::class) { dependsOn(pushLspd) + dependsOn(pushLspdNative) dependsOn(killLspd) commandLine(adb, "shell", "su", "-c", "sh /data/adb/modules/riru_lsposed/service.sh&") isIgnoreExitValue = true diff --git a/core/magisk_module/customize.sh b/core/magisk_module/customize.sh index 9cdce4e8..9c3457e5 100644 --- a/core/magisk_module/customize.sh +++ b/core/magisk_module/customize.sh @@ -83,6 +83,9 @@ if [ "$ARCH" = "arm" ] || [ "$ARCH" = "arm64" ]; then if [ "$IS64BIT" = true ]; then ui_print "- Extracting arm64 libraries" extract "$ZIPFILE" "lib/arm64-v8a/lib$RIRU_MODULE_LIB_NAME.so" "$MODPATH/riru/lib64" true + extract "$ZIPFILE" 'lib/arm64-v8a/libdaemon.so' "$MODPATH" true + else + extract "$ZIPFILE" 'lib/armeabi-v7a/libdaemon.so' "$MODPATH" true fi fi @@ -93,21 +96,24 @@ if [ "$ARCH" = "x86" ] || [ "$ARCH" = "x64" ]; then if [ "$IS64BIT" = true ]; then ui_print "- Extracting x64 libraries" extract "$ZIPFILE" "lib/x86_64/lib$RIRU_MODULE_LIB_NAME.so" "$MODPATH/riru/lib64" true + extract "$ZIPFILE" 'lib/x86_64/libdaemon.so' "$MODPATH" true + else + extract "$ZIPFILE" 'lib/x86/libdaemon.so' "$MODPATH" true fi fi if [ "$RIRU_MODULE_DEBUG" = true ]; then mv "$MODPATH/riru" "$MODPATH/system" - mv "$MODPATH/system/lib/liblspd.so" "$MODPATH/system/lib/libriru_lspd.so" - mv "$MODPATH/system/lib64/liblspd.so" "$MODPATH/system/lib64/libriru_lspd.so" + mv "$MODPATH/system/lib/lib$RIRU_MODULE_LIB_NAME.so" "$MODPATH/system/lib/libriru_$RIRU_MODULE_LIB_NAME.so" + mv "$MODPATH/system/lib64/lib$RIRU_MODULE_LIB_NAME.so" "$MODPATH/system/lib64/libriru_$RIRU_MODULE_LIB_NAME.so" mv "$MODPATH/framework" "$MODPATH/system/framework" if [ "$RIRU_API" -ge 26 ]; then mkdir -p "$MODPATH/riru/lib" mkdir -p "$MODPATH/riru/lib64" - touch "$MODPATH/riru/lib/libriru_lspd" - touch "$MODPATH/riru/lib64/libriru_lspd" + touch "$MODPATH/riru/lib/libriru_$RIRU_MODULE_LIB_NAME" + touch "$MODPATH/riru/lib64/libriru_$RIRU_MODULE_LIB_NAME" else - mkdir -p "/data/adb/riru/modules/lspd" + mkdir -p "/data/adb/riru/modules/$RIRU_MODULE_LIB_NAME" fi fi diff --git a/core/magisk_module/lspd b/core/magisk_module/lspd index c093d9bd..a9fe06b7 100644 --- a/core/magisk_module/lspd +++ b/core/magisk_module/lspd @@ -6,12 +6,15 @@ debug="false" if [ -r $tmpLspdDex ]; then java_options="-Djava.class.path=$tmpLspdDex" + java_options="$java_options -Djava.library.path=/data/local/tmp" debug="true" elif [ -d "$dir/system" ]; then java_options="-Djava.class.path=$dir/system/framework/lspd.dex" + java_options="$java_options -Djava.library.path=$dir" debug="true" else java_options="-Djava.class.path=$dir/framework/lspd.dex" + java_options="$java_options -Djava.library.path=$dir" fi if [ $debug = "true" ]; then diff --git a/core/magisk_module/post-fs-data.sh b/core/magisk_module/post-fs-data.sh index 64187126..03c8f02e 100644 --- a/core/magisk_module/post-fs-data.sh +++ b/core/magisk_module/post-fs-data.sh @@ -20,105 +20,12 @@ # Copyright (C) 2021 LSPosed Contributors # -grep_prop() { - local REGEX="s/^$1=//p" - shift - local FILES="$@" - [ -z "$FILES" ] && FILES='/system/build.prop' - sed -n "$REGEX" ${FILES} 2>/dev/null | head -n 1 -} - MODDIR=${0%/*} -MAGISK_VERSION=$(magisk -v) -MAGISK_VER_CODE=$(magisk -V) - -[ ! -f $(magisk --path)/.magisk/modules/riru-core/util_functions.sh ] && exit 1 -. $(magisk --path)/.magisk/modules/riru-core/util_functions.sh - -LSPD_VERSION=$(grep_prop version "${MODDIR}/module.prop") - -ANDROID_SDK=$(getprop ro.build.version.sdk) -BUILD_DESC=$(getprop ro.build.description) -PRODUCT=$(getprop ro.build.product) -MODEL=$(getprop ro.product.model) -MANUFACTURER=$(getprop ro.product.manufacturer) -BRAND=$(getprop ro.product.brand) -FINGERPRINT=$(getprop ro.build.fingerprint) -ARCH=$(getprop ro.product.cpu.abi) -DEVICE=$(getprop ro.product.device) -ANDROID=$(getprop ro.build.version.release) -BUILD=$(getprop ro.build.id) - MISC_PATH=$(cat /data/adb/lspd/misc_path) BASE_PATH="/data/misc/$MISC_PATH" LOG_PATH="/data/adb/lspd/log" -ENABLE_VERBOSE_LOG_FILE="/data/adb/lspd/config/verbose_log" -LOG_VERBOSE=false - -if [ "$(cat "${ENABLE_VERBOSE_LOG_FILE}")" = "1" ]; then - LOG_VERBOSE=true -fi - -# If logcat client is kicked out by klogd server, we'll restart it. -# However, if it is killed manually or by LSPosed Manager, we'll exit. -loop_logcat() { - while true; do - logcat $* - if [ $? -ne 1 ]; then - break - fi - done -} - -print_log_head() { - LOG_FILE=$1 - touch "${LOG_FILE}" - chmod 666 "${LOG_FILE}" - { - echo "LSPosed Log" - echo "--------- beginning of information" - echo "Manufacturer: ${MANUFACTURER}" - echo "Brand: ${BRAND}" - echo "Device: ${DEVICE}" - echo "Product: ${PRODUCT}" - echo "Model: ${MODEL}" - echo "Fingerprint: ${FINGERPRINT}" - echo "ROM description: ${BUILD_DESC}" - echo "Architecture: ${ARCH}" - echo "Android build: ${BUILD}" - echo "Android version: ${ANDROID}" - echo "Android sdk: ${ANDROID_SDK}" - echo "LSPosed version: ${LSPD_VERSION}" - echo "Riru version: ${RIRU_VERSION_NAME} (${RIRU_VERSION_CODE})" - echo "Riru api: ${RIRU_API}" - echo "Magisk: ${MAGISK_VERSION%:*} (${MAGISK_VER_CODE})" - } >>"${LOG_FILE}" -} - -start_log_catcher() { - LOG_FILE_NAME=$1 - LOG_TAG_FILTERS=$2 - CLEAN_OLD=$3 - START_NEW=$4 - LOG_FILE="${LOG_PATH}/${LOG_FILE_NAME}.log" - PID_FILE="${LOG_PATH}/${LOG_FILE_NAME}.pid" - mkdir -p ${LOG_PATH} - if [ ${CLEAN_OLD} == true ]; then - rm "${LOG_FILE}.old" - mv "${LOG_FILE}" "${LOG_FILE}.old" - fi - rm "${LOG_PATH}/${LOG_FILE_NAME}.pid" - if [ ${START_NEW} == false ]; then - return - fi - touch "${PID_FILE}" - print_log_head "${LOG_FILE}" - loop_logcat -f "${LOG_FILE}" *:S "${LOG_TAG_FILTERS}" & - LOG_PID=$! - echo "${LOG_PID}" >"${LOG_PATH}/${LOG_FILE_NAME}.pid" -} chcon -R u:object_r:system_file:s0 "${MODDIR}" chcon -R u:object_r:system_file:s0 "/data/adb/lspd" @@ -130,9 +37,6 @@ 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}" - print_log_head "${LOG_PATH}/modules.log" - # start_verbose_log_catcher - start_log_catcher all "LSPosed:V XSharedPreferences:V LSPosed-Bridge:V LSPosedManager:V LSPosedService:V *:F" true ${LOG_VERBOSE} fi rm -f "/data/local/tmp/lspd.dex" diff --git a/core/proguard-rules.pro b/core/proguard-rules.pro index 06987f98..5cedf3e5 100644 --- a/core/proguard-rules.pro +++ b/core/proguard-rules.pro @@ -15,6 +15,9 @@ public static boolean execTransact(int, long, long, int); public static android.os.IBinder getApplicationServiceForSystemServer(android.os.IBinder, android.os.IBinder); } +-keepclasseswithmembers class org.lsposed.lspd.service.LogcatService { + private int refreshFd(); +} -assumenosideeffects class android.util.Log { public static *** v(...); public static *** d(...); diff --git a/core/src/main/cpp/Android.mk b/core/src/main/cpp/Android.mk index cba3d605..dc699f87 100644 --- a/core/src/main/cpp/Android.mk +++ b/core/src/main/cpp/Android.mk @@ -1,6 +1,7 @@ include src/main/cpp/external/DexBuilder/Android.mk include src/main/cpp/external/yahfa/Android.mk include src/main/cpp/main/Android.mk +include src/main/cpp/daemon/Android.mk $(call import-module,prefab/cxx) $(call import-module,prefab/riru) diff --git a/core/src/main/cpp/daemon/Android.mk b/core/src/main/cpp/daemon/Android.mk new file mode 100644 index 00000000..8b614000 --- /dev/null +++ b/core/src/main/cpp/daemon/Android.mk @@ -0,0 +1,9 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := daemon +LOCAL_SRC_FILES := logcat.cpp +LOCAL_STATIC_LIBRARIES := cxx +LOCAL_ALLOW_UNDEFINED_SYMBOLS := true +LOCAL_LDLIBS := -llog +include $(BUILD_SHARED_LIBRARY) diff --git a/core/src/main/cpp/daemon/logcat.cpp b/core/src/main/cpp/daemon/logcat.cpp new file mode 100644 index 00000000..93b79ac7 --- /dev/null +++ b/core/src/main/cpp/daemon/logcat.cpp @@ -0,0 +1,155 @@ +#include +#include +#include +#include +#include + +#include "logcat.h" + +class UniqueFile : public std::unique_ptr> { + inline static deleter_type deleter = [](auto f) { f && f != stdout && fclose(f); }; +public: + explicit UniqueFile(FILE *f) : std::unique_ptr>(f, deleter) {} + + UniqueFile(int fd, const char *mode) : UniqueFile(fd > 0 ? fdopen(fd, mode) : stdout) {}; + + UniqueFile() : UniqueFile(nullptr) {}; +}; + +class Logcat { +public: + explicit Logcat(JNIEnv *env, jobject thiz, jmethodID method, jlong tid) : + env_(env), thiz_(thiz), refresh_fd_method_(method), tid_(tid) { RefreshFd(); }; + + void Run(); + +private: + void RefreshFd(); + + bool ProcessBuffer(struct log_msg *buf); + + static char FilterPriToChar(android_LogPriority pri); + + static void PrintLogLine(FILE *out, const AndroidLogEntry *entry); + + JNIEnv *env_; + jobject thiz_; + jmethodID refresh_fd_method_; + jlong tid_; + + UniqueFile out_file_{}; + size_t print_count_ = 0; + + constexpr static size_t kMaxCount = 500; + constexpr static uint32_t kSetLogSize = 32 * 1024 * 1024; +}; + +char Logcat::FilterPriToChar(android_LogPriority pri) { + switch (pri) { + case ANDROID_LOG_VERBOSE: + return 'V'; + case ANDROID_LOG_DEBUG: + return 'D'; + case ANDROID_LOG_INFO: + return 'I'; + case ANDROID_LOG_WARN: + return 'W'; + case ANDROID_LOG_ERROR: + return 'E'; + case ANDROID_LOG_FATAL: + return 'F'; + case ANDROID_LOG_SILENT: + return 'S'; + case ANDROID_LOG_DEFAULT: + case ANDROID_LOG_UNKNOWN: + default: + return '?'; + } +} + +void Logcat::PrintLogLine(FILE *out, const AndroidLogEntry *entry) { + if (!out) return; + constexpr static size_t kMaxTimeBuff = 64; + struct tm tm{}; + std::array time_buff; + + auto now = entry->tv_sec; + auto nsec = entry->tv_nsec; + if (now < 0) { + nsec = NS_PER_SEC - nsec; + } + localtime_r(&now, &tm); + strftime(time_buff.data(), time_buff.size(), "%Y-%m-%dT%H:%M:%S", &tm); + fprintf(out, "[ %s.%03ld %8d:%6d:%6d %c/%-15.*s ] %.*s\n", time_buff.data(), nsec / MS_PER_NSEC, + entry->uid, entry->pid, entry->tid, + FilterPriToChar(entry->priority), static_cast(entry->tagLen), entry->tag, + static_cast(entry->messageLen), entry->message); +} + +void Logcat::RefreshFd() { + out_file_ = UniqueFile(env_->CallIntMethod(thiz_, refresh_fd_method_), "w"); +} + +bool Logcat::ProcessBuffer(struct log_msg *buf) { + int err; + AndroidLogEntry entry; + err = android_log_processLogBuffer(&buf->entry, &entry); + if (err < 0) return false; + + std::string_view tag(entry.tag); + if (buf->id() == log_id::LOG_ID_CRASH || + tag == "Magisk" || + tag.starts_with("Riru") || + tag.starts_with("LSPosed") || + tag == "XSharedPreferences") { + ++print_count_; + PrintLogLine(out_file_.get(), &entry); + } + return entry.pid == getpid() && + tag == "LSPosedLogcat" && + std::string_view(entry.message) == "!!stop!!" + std::to_string(tid_); +} + +void Logcat::Run() { + std::unique_ptr logger_list{ + nullptr, &android_logger_list_free}; + + logger_list.reset(android_logger_list_alloc(0, 0, 0)); + + for (log_id id:{LOG_ID_MAIN, LOG_ID_CRASH}) { + auto *logger = android_logger_open(logger_list.get(), id); + if (logger == nullptr) { + continue; + } + android_logger_set_log_size(logger, kSetLogSize); + } + + while (true) { + struct log_msg msg{}; + int ret = android_logger_list_read(logger_list.get(), &msg); + + if (ret <= 0) { + continue; + } + + if (ProcessBuffer(&msg)) { + break; + } + + fflush(out_file_.get()); + + if (print_count_ > kMaxCount) { + RefreshFd(); + print_count_ = 0; + } + } +} + +extern "C" +JNIEXPORT void JNICALL +Java_org_lsposed_lspd_service_LogcatService_runLogcat(JNIEnv *env, jobject thiz, jlong tid) { + jclass clazz = env->GetObjectClass(thiz); + jmethodID method = env->GetMethodID(clazz, "refreshFd", "()I"); + Logcat logcat(env, thiz, method, tid); + logcat.Run(); +} diff --git a/core/src/main/cpp/daemon/logcat.h b/core/src/main/cpp/daemon/logcat.h new file mode 100644 index 00000000..b814a2c4 --- /dev/null +++ b/core/src/main/cpp/daemon/logcat.h @@ -0,0 +1,62 @@ +#pragma once + +#include +#include + +#include + +#define NS_PER_SEC 1000000000ULL +#define MS_PER_NSEC 1000000 +#define LOGGER_ENTRY_MAX_LEN (5 * 1024) + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct AndroidLogEntry_t { + time_t tv_sec; + long tv_nsec; + android_LogPriority priority; + int32_t uid; + int32_t pid; + int32_t tid; + const char *tag; + size_t tagLen; + size_t messageLen; + const char *message; +} AndroidLogEntry; + +struct logger_entry { + uint16_t len; /* length of the payload */ + uint16_t hdr_size; /* sizeof(struct logger_entry) */ + int32_t pid; /* generating process's pid */ + uint32_t tid; /* generating process's tid */ + uint32_t sec; /* seconds since Epoch */ + uint32_t nsec; /* nanoseconds */ + uint32_t lid; /* log id of the payload, bottom 4 bits currently */ + uint32_t uid; /* generating process's uid */ +}; + +struct log_msg { + union alignas(4) { + unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1]; + struct logger_entry entry; + }; +#ifdef __cplusplus + log_id_t id() { + return static_cast(entry.lid); + } +#endif +}; +struct logger; +struct logger_list; + +int android_logger_set_log_size(struct logger *logger, unsigned long size); +struct logger_list *android_logger_list_alloc(int mode, unsigned int tail, pid_t pid); +void android_logger_list_free(struct logger_list *logger_list); +int android_logger_list_read(struct logger_list *logger_list, struct log_msg *log_msg); +struct logger *android_logger_open(struct logger_list *logger_list, log_id_t id); +int android_log_processLogBuffer(struct logger_entry *buf, AndroidLogEntry *entry); +#ifdef __cplusplus +} +#endif diff --git a/core/src/main/java/org/lsposed/lspd/core/Main.java b/core/src/main/java/org/lsposed/lspd/core/Main.java index b62e6af1..96f529f8 100644 --- a/core/src/main/java/org/lsposed/lspd/core/Main.java +++ b/core/src/main/java/org/lsposed/lspd/core/Main.java @@ -22,7 +22,6 @@ package org.lsposed.lspd.core; import static org.lsposed.lspd.config.LSPApplicationServiceClient.serviceClient; -import android.annotation.SuppressLint; import android.app.ActivityThread; import android.app.LoadedApk; import android.content.pm.ApplicationInfo; @@ -48,7 +47,6 @@ import de.robv.android.xposed.XposedBridge; import de.robv.android.xposed.XposedHelpers; import de.robv.android.xposed.XposedInit; -@SuppressLint("DefaultLocale") public class Main { public static void startBootstrapHook(boolean isSystem, String appDataDir) { Utils.logD("startBootstrapHook starts: isSystem = " + isSystem); 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 8c71063f..783262e6 100644 --- a/core/src/main/java/org/lsposed/lspd/service/ConfigManager.java +++ b/core/src/main/java/org/lsposed/lspd/service/ConfigManager.java @@ -96,8 +96,7 @@ public class ConfigManager { private static final File resourceHookSwitch = new File(configPath, "enable_resources"); private boolean resourceHook = false; - private static final File verboseLogSwitch = new File(configPath, "verbose_log"); - private boolean verboseLog = false; + private boolean logcat = true; private static final File managerPath = new File(configPath, "manager"); private String manager = null; @@ -107,9 +106,8 @@ public class ConfigManager { private String miscPath = null; private static final File logPath = new File(basePath, "log"); - private static final File modulesLog = new File(logPath, "modules.log"); - private static final File oldModulesLog = new File(logPath, "modules.old.log"); - private static final File verboseLogPath = new File(logPath, "all.log"); + private static final File modulesLog = new File(logPath, "modules.txt"); + private static final File oldModulesLog = new File(logPath, "modules.old.txt"); static class FileLocker { private final FileChannel lockChannel; @@ -136,7 +134,7 @@ public class ConfigManager { static { try { - Files.createDirectories(basePath.toPath()); + Files.createDirectories(logPath.toPath()); } catch (IOException e) { Log.e(TAG, Log.getStackTraceString(e)); } @@ -310,7 +308,6 @@ public class ConfigManager { private synchronized void updateConfig() { resourceHook = readInt(resourceHookSwitch, 0) == 1; - verboseLog = readInt(verboseLogSwitch, 0) == 1; miscPath = "/data/misc/" + readText(miscFile, "lspd"); updateManager(); } @@ -870,9 +867,14 @@ public class ConfigManager { this.resourceHook = resourceHook; } - public void setVerboseLog(boolean verboseLog) { - writeInt(verboseLogSwitch, verboseLog ? 1 : 0); - this.verboseLog = verboseLog; + public void setVerboseLog(boolean on) { + var logcatService = ServiceManager.getLogcatService(); + if (on) { + logcatService.start(); + } else { + logcatService.stop(); + } + logcat = on; } public boolean resourceHook() { @@ -880,7 +882,11 @@ public class ConfigManager { } public boolean verboseLog() { - return verboseLog; + return logcat; + } + + public static File getLogPath() { + return logPath; } public ParcelFileDescriptor getModulesLog(int mode) { @@ -898,7 +904,8 @@ public class ConfigManager { public ParcelFileDescriptor getVerboseLog() { try { - return ParcelFileDescriptor.open(verboseLogPath, ParcelFileDescriptor.MODE_READ_ONLY); + var logcat = ServiceManager.getLogcatService().getLog(); + return ParcelFileDescriptor.open(logcat, ParcelFileDescriptor.MODE_READ_ONLY); } catch (FileNotFoundException e) { Log.e(TAG, Log.getStackTraceString(e)); return null; @@ -907,7 +914,8 @@ public class ConfigManager { public boolean clearLogs(boolean verbose) { try { - OutputStream os = new FileOutputStream(verbose ? verboseLogPath : modulesLog); + var logcat = ServiceManager.getLogcatService().getLog(); + OutputStream os = new FileOutputStream(verbose ? logcat : modulesLog); os.close(); return true; } catch (IOException e) { diff --git a/core/src/main/java/org/lsposed/lspd/service/LogcatService.java b/core/src/main/java/org/lsposed/lspd/service/LogcatService.java new file mode 100644 index 00000000..5c453e4b --- /dev/null +++ b/core/src/main/java/org/lsposed/lspd/service/LogcatService.java @@ -0,0 +1,75 @@ +package org.lsposed.lspd.service; + +import android.os.ParcelFileDescriptor; +import android.os.SystemProperties; +import android.util.Log; + +import java.io.File; +import java.io.FileNotFoundException; +import java.time.Instant; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; + +public class LogcatService implements Runnable { + private static final String TAG = "LSPosedLogcat"; + private Thread thread; + private final File logPath; + private File log = null; + private static DateTimeFormatter logTimeFormat; + + public LogcatService(File logPath) { + System.loadLibrary("daemon"); + this.logPath = logPath; + var zone = ZoneId.of(SystemProperties.get("persist.sys.timezone", "GMT")); + logTimeFormat = DateTimeFormatter.ISO_LOCAL_DATE_TIME.withZone(zone); + } + + @Override + public void run() { + Log.i(TAG, "start running"); + runLogcat(thread.getId()); + Log.i(TAG, "stoped"); + } + + private native void runLogcat(long tid); + + @SuppressWarnings("unused") + private int refreshFd() { + if (log == null || log.length() > 32 * 1024 * 1024) { + log = new File(logPath, logTimeFormat.format(Instant.now()) + ".log"); + } + + var mode = ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_CREATE | + ParcelFileDescriptor.MODE_TRUNCATE | ParcelFileDescriptor.MODE_APPEND; + try { + return ParcelFileDescriptor.open(log, mode).detachFd(); + } catch (FileNotFoundException e) { + Log.w(TAG, "someone chattr +i ?", e); + return -1; + } + } + + public void start() { + if (isRunning()) return; + thread = new Thread(this); + thread.setName("logcat"); + thread.setUncaughtExceptionHandler((t, e) -> { + Log.e(TAG, "Crash unexpectedly: ", e); + thread = null; + }); + thread.start(); + } + + public void stop() { + // logcat thread is listening for this keyword + Log.i(TAG, "!!stop!!" + thread.getId()); + } + + public boolean isRunning() { + return thread != null && thread.isAlive(); + } + + public File getLog() { + return log; + } +} 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 5a152304..055b53fd 100644 --- a/core/src/main/java/org/lsposed/lspd/service/ServiceManager.java +++ b/core/src/main/java/org/lsposed/lspd/service/ServiceManager.java @@ -37,13 +37,14 @@ import java.util.concurrent.ConcurrentHashMap; import hidden.HiddenApiBridge; 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"); private static LSPosedService mainService = null; - final private static ConcurrentHashMap moduleServices = new ConcurrentHashMap<>(); private static LSPApplicationService applicationService = null; private static LSPManagerService managerService = null; private static LSPSystemServerService systemServerService = null; - public static final String TAG = "LSPosedService"; - private static final File globalNamespace = new File("/proc/1/root"); + private static LogcatService logcatService = null; private static void waitSystemService(String name) { while (android.os.ServiceManager.getService(name) == null) { @@ -77,6 +78,9 @@ public class ServiceManager { System.exit(1); }); + logcatService = new LogcatService(ConfigManager.getLogPath()); + logcatService.start(); + Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND); Looper.prepareMainLooper(); mainService = new LSPosedService(); @@ -137,6 +141,10 @@ public class ServiceManager { return managerService; } + public static LogcatService getLogcatService() { + return logcatService; + } + public static boolean systemServerRequested() { return systemServerService.systemServerRequested(); } diff --git a/core/src/main/java/org/lsposed/lspd/util/ModuleLogger.java b/core/src/main/java/org/lsposed/lspd/util/ModuleLogger.java index 68a12b65..eed617a7 100644 --- a/core/src/main/java/org/lsposed/lspd/util/ModuleLogger.java +++ b/core/src/main/java/org/lsposed/lspd/util/ModuleLogger.java @@ -30,6 +30,7 @@ import java.io.IOException; import java.time.Instant; import java.time.ZoneId; import java.time.format.DateTimeFormatter; +import java.util.Locale; public class ModuleLogger { private static DateTimeFormatter logDateFormat; @@ -39,7 +40,8 @@ public class ModuleLogger { if (fd == null && fileDescriptor != null) { fd = fileDescriptor; var zone = ZoneId.of(SystemProperties.get("persist.sys.timezone", "GMT")); - logDateFormat = DateTimeFormatter.ISO_LOCAL_DATE_TIME.withZone(zone); + var pattern = "uuuu-MM-dd'T'HH:mm:ss.SSS"; // DateTimeFormatter.ISO_LOCAL_DATE_TIME + logDateFormat = DateTimeFormatter.ofPattern(pattern, Locale.ROOT).withZone(zone); } } @@ -48,24 +50,16 @@ public class ModuleLogger { Utils.logE("Logger is not initialized"); return; } - StringBuilder sb = new StringBuilder(); String processName = ActivityThread.currentProcessName(); - - sb.append(logDateFormat.format(Instant.now())); - sb.append(' '); - sb.append(isThrowable ? "E" : "I"); - sb.append('/'); - sb.append(processName == null ? "android" : processName); - sb.append('('); - sb.append(Process.myPid()); - sb.append('-'); - sb.append(Process.myTid()); - sb.append(')'); - sb.append(": "); - sb.append(str); - sb.append('\n'); + var log = String.format(Locale.ROOT, "[ %s %5d:%5d:%5d %c/%s ] %s\n", + logDateFormat.format(Instant.now()), + Process.myUid(), + Process.myPid(), + Process.myTid(), + isThrowable ? 'E' : 'I', + processName == null ? "android" : processName, + str); try { - var log = sb.toString(); var writer = new FileWriter(fd.getFileDescriptor()); writer.write(log, 0, log.length()); writer.flush();